I meant "great article but..." :)
On 18 Nov 2010 17:23, "Josh Canfield" <[email protected]> wrote:
> I'm pretty sure that the footnote "Tapestry does _not_ use reflection to
> implement property expressions." is incorrect.
>
> As I understand it, property expressions are built using the
> PropertyConduitSource which uses reflection to determine the type and
> available properties for each step in the expression. Right or wrong, I'm
> not sure why the distinction is made here anyway...
>
> Josh
>
> On Thu, Nov 18, 2010 at 4:46 PM, <[email protected]> wrote:
>
>> First <https://cwiki.apache.org/confluence/display/TAPESTRY/First> Page
>> *edited* by Howard M. Lewis Ship<
https://cwiki.apache.org/confluence/display/~hlship>
>> Changes (39)
>> ...
>> 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.
Tapestry
>> expects pages to be stored in the pages package under your root package;
in
>> this case, the class name will be com.example.tutorial.pages.Index.
>>
>> Tapestry pages are the combination of a POJO Java class with a Tapestry
>> component template. The 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/resource/com/example/tutorial/pages/Index.tml.
>> h3. Index Java Class
>>
>> The final piece of the puzzle is the Java class for the page. Tapestry
has
>> very specific rules for where page classes go. Remember the package name
>> (configured inside web.xml)? Tapestry adds a sub-package, "pages", to it
and
>> the Java class goes there. Thus the full Java class name is
>> org.apache.tapestry5.tutorial.pages.Index.
>>
>> {code:title=src/main/java/com/example/tutorial/pages/Index.java}
>> package org.apache.tapestry5.tutorial.pages;
>>
>> import java.util.Date;
>>
>> /**
>> * Start page of application tutorial1.
>> */
>> public class Index
>> {
>> public Date getCurrentTime()
>> {
>> return new Date();
>> }
>> }
>> {code}
>>
>> That's pretty darn simple: No classes to extend, no interfaces to
>> implement, just a very pure POJO (Plain Old Java Object). You do have to
>> meet the Tapestry framework halfway:
>>
>> * You need to put the Java class in the expected package,
>> org.apache.tapestry5.tutorial.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)
>>
>> As we saw when running the application, this page displays the current
date
>> and time. the {{currentTime}} property is where that value comes from;
>> shortly we'll see how that value is 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 [Model-View-Controller pattern|
>> http://en.wikipedia.org/wiki/Model_view_controller] (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.
>>
>> h3. Component Template
>>
>> Tapestry pages are the combination of a POJO Java class with a Tapestry
>> component template. The 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/resource/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.
>>
>> 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.
>>
>> ...
>> {code}
>>
>> {warning}
>> 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 Windows) and not on
>> others (Mac OS X, 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.
>> {warning}
>>
>> The goal in Tapestry is for component templates, such as Index.tml, to
look
>> as much as possible like ordinary, static HTML files{footnote}By static,
we
>> mean unchanging, as opposed to a dynamically generated Tapestry
>> page.{footnote}. 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 the XML
>> namespaces. By convention, the prefix "t:" is used for the primary
>> namespace, but that is not a requirement.
>>
>> This short template demonstrates quite a few features of Tapestry.
>> ...
>> The second namespace, "p:", is a way of marking a chunk of the template
as
>> a parameter passed into another component. We'll expand on that shortly.
>>
>> A Tapestry 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_.
>>
>> Components can be represented two ways:
>> h2. 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:
>>
>> {code:lang=xml}
>> <p>The current time is: ${currentTime}</p>
>> {code}
>>
>> {info}
>> If you are coming to Tapestry 5 from Tapestry 4 or earlier, expansions
are
>> a concise replacement for the Insert component.
>> {info}
>>
>> The value inside the curly braces is a _property expression_. Tapestry
uses
>> its own property expression language that is expressive, fast, and
>> type-safe{footnote}Tapestry does _not_ use reflection to implement
property
>> expressions.{footnote}. 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{footnote}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.{footnote}.
>>
>> 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
>> concert 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:
>>
>> {code:lang=xml}
>> <p>${message:greeting}</p>
>> {code}
>>
>> Here {{greeting}} is not a property of the page; its actually a localized
>> message key. Every page and component is allowed to have its own message
>> catalog.
>>
>>
{code:title=src/main/resources/com/example/tutorial/pages/Index.properties}
>>
>> greeting=Welcome to Tapestry 5! We hope that this project template will
get
>> you going in style.
>> {code}
>>
>> 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 later). 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.
>>
>> 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.
>>
>> h2. Components Inside Templates
>>
>> Components can be represented inside a component template in two
>> ways{footnote}Ok, there's a third way as well, which will be discussed in
>> good time.{footnote}:
>>
>> * As an ordinary element, but with a t:type attribute to define the type
of
>> component.
>>
>> * As an element in the Tapestry namespace, in which case the element name
>> determines the type.
>>
>> In nearly all cases, the two approaches are equivalent.
>>
>> Here we've used an <html> element to represent the application's Layout
>> component.
>>
>> ...
>> {code}
>>
>> Which form you use select is a matter of choice. In the vast majority of
>> cases, they are exactly equivalent.
>>
>> {note}
>> ...
>> This binds two parameters, {{title}} and {{sidebarTitle}} of the Layout
>> component, to the literal strings "tutorial1 Index" and "Current Time",
>> 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 bit. The point is,
the
>> page's template is integrated into the Layout components. The following
>> diagram shows how parameters passed to the Layout component end up
rendered
>> in the final page:
>>
>> {gliffy:name=Templates and Parameters|align=center|version=2}
>>
>> 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.
>>
>> {code:lang=xml}
>>
>>
>>
>> First is the way we display the current date and time: $\{currentTime\}.
>> This syntax is used to access a property of the page object, a property
>> named {{currentTime}}. Tapestry calls this an _expansion_. The value
inside
>> the braces is the name of a standard JavaBeans property supplied by the
>> page. As we'll see in later chapters, this is just the tip of the iceberg
>> for what is possible using expansions.
>>
>> The other dynamic element is the link used to refresh the page. We're
>> specifying a component as an XML _element_ within the Tapestry namespace.
>> The element name, "pagelink", defines the type of component. PageLink
>> (Tapestry is case insensitive) is a component built into the framework;
it
>> is part of the Tapestry core component library. The attribute, page, is a
>> string - the name of the page to link to. Here, we're linking back to the
>> same page, page "Index".
>>
>> This is how Tapestry works; the Index page contains an _instance_ of the
>> PageLink component. The PageLink component is configured via its
parameters,
>> which controls what it does and how it behaves.
>>
>> The URL that the PageLink component will render out is [
>> http://localhost:8080/tapestry-tutorial1/]. "Index" pages are special and
>> are identified just by the folder name. In later examples, when we link
to
>> pages besides "Index", the page name will be part of the URL.
>>
>> Tapestry ignores case where ever it can. Inside the template, we
configured
>> the PageLink component's page parameter with the name of the page,
"Index".
>> Here too we could be fuzzy on case. Feel free to use "index" if that
works
>> for you.
>>
>> {warning}
>> 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 Windows) and not on
>> others (Mac OS X, 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.
>> {warning}
>>
>> Clicking the link in the web browser sends a request to re-render the
page;
>> the template and Java object are re-used to generate the HTML sent to the
>> browser, which results in the updated time showing up in the web browser.
>>
>> The final piece of the puzzle is the Java class for the page. Tapestry
has
>> very specific rules for where page classes go. Remember the package name
>> (configured inside web.xml)? Tapestry adds a sub-package, "pages", to it
and
>> the Java class goes there. Thus the full Java class name is
>> org.apache.tapestry5.tutorial.pages.Index.
>>
>> {code:title=src/main/java/org/apache/tapestry5/tutorial/pages/Index.java}
>> package org.apache.tapestry5.tutorial.pages;
>>
>> import java.util.Date;
>>
>> /**
>> * Start page of application tutorial1.
>> */
>> public class Index
>> {
>> public Date getCurrentTime()
>> {
>> return new Date();
>> }
>> }
>> <t:pagelink page="Index">refresh</t:pagelink>
>> {code}
>>
>> That's pretty darn simple: No classes to extend, no interfaces to
>> implement, just a very pure POJO (Plain Old Java Object). You do have to
>> meet the Tapestry framework halfway:
>> 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.
>>
>> * You need to put the Java class in the expected package,
>> org.apache.tapestry5.tutorial.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)
>> h2. Running the Application inside Eclipse
>>
>> The template referenced the property {{currentTime}} and we're providing
>> that as a property, as a _synthetic property_, a property that is
computed
>> on the fly (rather than stored in an instance variable).
>>
>> This means that every time the page renders, a fresh Date instance is
>> created, which is just what we want.
>>
>> As the page renders, it generates the HTML markup that is sent to the
>> client web browser. For most of the page, that markup is exactly what
came
>> out of the component template: this is called the _static content_ (we're
>> using the term "static" to mean "unchanging").
>>
>> The expansion, $\{currentTime\}, is _dynamic:_ different every time.
>> Tapestry will read that property and convert the result into a string,
and
>> that string is mixed into the stream of markup sent to the client.
\_We'll
>> often talk about the "client" and we don't mean the people you send your
>> invoices to: we're talking about the client web browser. Of course, in a
>> world of web spiders and other screen scrapers, there's no guarantee that
>> the thing on the other end of the HTTP pipe is really a web
>> browser.{footnote}You'll often see low-level HTML and HTTP documentation
>> talk about the "user agent" rather than the "browser".{footnote}.
Likewise,
>> the PageLink component is dynamic, in that it generates a URL that is
>> (potentially) different every time.
>>
>> 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 of the other of these methods,
the
>> property is either read only (as here), or write only{footnote}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.{footnote}.
>>
>> 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.
>>
>> In the next chapter, we'll start to build a simple hi-lo guessing game,
but
>> we've got one more task before then, plus a magic trick.
>>
>> ...
>> Choose the *Run ...* item from the Eclipse *Run* menu to get the launch
>> configuration dialog:
>>
>> !eclipse-run.png!
>> !eclipse-run.png|align=center,thumbnail!
>>
>> Select Jetty Web and click the _New_ button:
>> Select *Jetty Webapp* and click the *New* button, then fill in a few
>> values:
>>
>> !eclipse-launch.png!
>> !eclipse-launch.png|align=center,thumbnail!
>>
>> Make sure you clear the field labeled *HTTPS*.
>>
>> We've filled in a name for our launch configuration, and identified the
>> project. We've also told Jetty Launcher where our Jetty installation is.
>> We've identified the web context as src/main/webapp, and we've turned on
>> NCSA logging for good measure.
>> You can then click *Run* and Jetty will launch (it takes only a few
>> seconds):
>>
>> In addition, we've set up the context as "/tutorial1", which matches what
>> our eventual WAR file, tutorial1.war, would be deployed as inside an
>> application server.
>>
>> Once you click Run, Jetty will start up and launch (it should take about
>> two seconds).
>>
>> You may now start the application with the URL [
>> http://localhost:8080/tutorial1/|http://localhost:8080/tutorial1/<
http://localhost:8080/tutorial1/%7Chttp://localhost:8080/tutorial1/>].
>>
>> !eclipse-jetty.png|align=center,thumbnail!
>>
>> You may now start the application with the URL [
>> http://localhost:8080/tutorial1/].
>>
>> h2. A Magic Trick
>>
>> Now it's time for the magic trick. Edit Index.java and change the
>> {{getCurrentTime()}} method to:
>>
>> {code:lang=java|title=Index.java (partial)}
>> public String getCurrentTime()
>> {
>> ...
>> Make sure you save changes; then click the refresh link in the web
browser:
>>
>>
>> !app-live-reload.png!
>> !app-live-reload.png|align=center,thumbnail!
>>
>> This is one of Tapestry's early _wow factor_ features: changes to your
>> component classes are picked up immediately. 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.
>>
>> 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:
>>
>> !app-error-1.png|align=center,thumbnail!
>>
>> 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:
>>
>> !app-error-2.png|align=center,thumbnail!
>>
>> 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.
>>
>> Tapestry displays the stack trace of the deepest exception, along with
lots
>> of details about the runtime 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.
>>
>>
>> {info}
>> 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.
>> {info}
>>
>>
>> Now that we have our basic application set up, and ready to run (or
debug)
>> directly inside Eclipse, we can start working on implementing our Hi/Lo
game
>> in earnest.
>>
>> ...
>> Full Content
>> Chapter 2: Your First Tapestry Application
>> This chapter may look long, but almost all of it is one-time setup for
>> Maven and Eclipse. The actual Tapestry part is really small and simple.
>> Enjoy!
>>
>> Before we can get down to the fun, we have to create an empty
application.
>> Tapestry uses a feature of Maven to do this: *archetypes* (a too-clever
>> way of saying "project templates").
>>
>> What we'll do is create an empty shell application using Maven, then
import
>> the application into Eclipse to do the rest of the work.
>>
>> For the tutorial, I've used a fresh install of Eclipse and an empty
>> workspace at /Users/Howard/Documents/workspace 1
>> <https://cwiki.apache.org/confluence#Footnote1> . You may need to adjust
a
>> few things for other operating systems or local paths.
>>
>> From my workspace directory, I'll use Maven to create a skeleton Tapestry
>> project.
>>
>> Before proceeding, we have to decide on four things: A Maven *group
id*and
>> *artifact id* for our project, a *version*, and a *base package name*.
>>
>> Maven uses the group id and artifact id to provide a unique identity for
>> the application, and Tapestry needs to have a base package name so it
knows
>> where to look for pages and components.
>>
>> For this example, we'll use the group id *com.example*, artifact id *
>> tutorial1*, version *1.0-SNAPSHOT* and we'll use *com.example.tutorial*
as
>> the base package.
>>
>> Our final command line is:
>>
>> mvn archetype:generate -DarchetypeCatalog=http://tapestry.apache.org
>>
>> It will then prompt you to pick the archetype - choose *Tapestry 5.2.4
>> Quickstart Project*, enter the group id, artifact id, version and package
>> when prompted.
>>
>> ~/Documents/workspace
>> $ mvn archetype:generate -DarchetypeCatalog=http://tapestry.apache.org
>> [INFO] Scanning for projects...
>> [INFO] Searching repository for plugin with prefix: 'archetype'.
>> [INFO]
------------------------------------------------------------------------
>> [INFO] Building Maven Default Project
>> [INFO] task-segment: [archetype:generate] (aggregator-style)
>> [INFO]
------------------------------------------------------------------------
>> [INFO] Preparing archetype:generate
>> [INFO] No goals needed for project - skipping
>> [INFO] [archetype:generate {execution: default-cli}]
>> [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 -> quickstart (Tapestry 5.2.4 Quickstart
Project)
>> 2: http://tapestry.apache.org -> tapestry-archetype (Tapestry 4.1.6
Archetype)
>> Choose a number: : 1
>> Choose version:
>> 1: 5.1.0.5
>> 2: 5.0.19
>> 3: 5.2.4
>> Choose a number: 3: 3
>> Define value for property 'groupId': : com.example
>> Define value for property 'artifactId': : tutorial1
>> Define value for property 'version': 1.0-SNAPSHOT:
>> Define value for property 'package': com.example: com.example.tutorial
>> Confirm properties configuration:
>> groupId: com.example
>> artifactId: tutorial1
>> version: 1.0-SNAPSHOT
>> package: com.example.tutorial
>> Y:
>> [INFO]
------------------------------------------------------------------------
>> [INFO] BUILD SUCCESSFUL
>> [INFO]
------------------------------------------------------------------------
>> [INFO] Total time: 1 minute 41 seconds
>> [INFO] Finished at: Wed Nov 17 17:00:16 PST 2010
>> [INFO] Final Memory: 16M/81M
>> [INFO]
------------------------------------------------------------------------
>> ~/Documents/workspace
>> $
>>
>> The first time you use Maven, you'll see quite a bit more output, mostly
>> about downloading all sorts of JARs and other files. 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 executing the command, you'll see a new directory, tutorial1.
>> *Maven Behind a Firewall*
>> If you are behind a firewall, before running any "mvn" commands, you will
>> need to configure your proxy settings in settings.xml. Here is an
example:
>> *settings.xml*
>>
>> <settings>
>> <proxies>
>> <proxy>
>> <active>true</active>
>> <protocol>http</protocol>
>> <host>myProxyServer.com</host>
>> <port>8080</port>
>> <username>joeuser</username>
>> <password>myPassword</password>
>> <nonProxyHosts></nonProxyHosts>
>> </proxy>
>> </proxies>
>> <localRepository>C:/Documents and
Settings/joeuser/.m2/repository</localRepository>
>> </settings>
>>
>> Of course, adjust the localRepository element to match the correct path
>> for your computer.
>> Running the New Application in Jetty
>>
>> One of the first things you can do is use Maven to run Jetty directly.
>>
>> Change into the newly created directory, and execute the command:
>>
>> mvn jetty: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.
>>
>> Once Jetty is initialized (which only takes a few seconds), you'll see
the
>> following in your console:
>>
>> URLRewriter: DEFINED
>> UpdateListenerHub: REAL
>> ValidateBindingFactory: DEFINED
>> ValidationConstraintGenerator: DEFINED
>> ValidationMessagesSource: DEFINED
>> ValidatorMacro: DEFINED
>> ValueEncoderSource: DEFINED
>>
>> 85.00% unrealized services (153/180)
>>
>> 2010-11-17 17:06:30.630::INFO: Started
[email protected]:8080
>> [INFO] Started Jetty Server
>>
>> You can now open a web browser to http://localhost:8080/tutorial1/ to see
>> the running application:
>>
>>
>> <
https://cwiki.apache.org/confluence/download/attachments/23340356/startpage.png
>
>>
>> The date and time in the middle of the page proves that this is a live
>> application.
>>
>> This is a complete little application; it doesn't do much, but it
>> demonstrate how to create a number of pages sharing a common layout, and
>> demonstrates some simple navigation.
>> Loading the Project into Eclipse
>>
>> Let's look at what Maven has generated for us. To do this, we're going to
>> load the project inside Eclipse and continue from there.
>>
>> Start by hitting Control-C in the Terminal window to close down Jetty.
>>
>> Next, we'll ask Maven to create our Eclipse project for us:
>>
>> $ mvn eclipse:eclipse -DdownloadSources=true
>> [INFO] Scanning for projects...
>> [INFO] Searching repository for plugin with prefix: 'eclipse'.
>> [INFO] org.apache.maven.plugins: checking for updates from
apache-snapshots
>> [INFO] org.codehaus.mojo: checking for updates from apache-snapshots
>> [INFO] artifact org.apache.maven.plugins:maven-eclipse-plugin: checking
for updates from apache-snapshots
>> [INFO] snapshot
org.apache.maven.plugins:maven-eclipse-plugin:2.9-SNAPSHOT: checking for
updates from apache-snapshots
>> Downloading:
http://repository.apache.org/snapshots//org/apache/maven/plugins/maven-eclipse-plugin/2.9-SNAPSHOT/maven-eclipse-plugin-2.9-20101117.070458-148.pom
>> 11K <
http://repository.apache.org/snapshots//org/apache/maven/plugins/maven-eclipse-plugin/2.9-SNAPSHOT/maven-eclipse-plugin-2.9-20101117.070458-148.pom11K>
downloaded (maven-eclipse-plugin-2.9-20101117.070458-148.pom)
>> Downloading:
http://repository.apache.org/snapshots//org/apache/maven/plugins/maven-eclipse-plugin/2.9-SNAPSHOT/maven-eclipse-plugin-2.9-20101117.070458-148.jar
>> 194K <
http://repository.apache.org/snapshots//org/apache/maven/plugins/maven-eclipse-plugin/2.9-SNAPSHOT/maven-eclipse-plugin-2.9-20101117.070458-148.jar194K>
downloaded (maven-eclipse-plugin-2.9-20101117.070458-148.jar)
>> [INFO]
------------------------------------------------------------------------
>> [INFO] Building tutorial1 Tapestry 5 Application
>> [INFO] task-segment: [eclipse:eclipse]
>> [INFO]
------------------------------------------------------------------------
>> [INFO] Preparing eclipse:eclipse
>> [INFO] No goals needed for project - skipping
>> [INFO] [eclipse:eclipse {execution: default-cli}]
>> [INFO] Using Eclipse Workspace: /Users/Howard/Documents/workspace
>> [INFO] Adding default classpath container:
org.eclipse.jdt.launching.JRE_CONTAINER
>> [INFO] Wrote settings to
/Users/Howard/Documents/workspace/tutorial1/.settings/org.eclipse.jdt.core.prefs
>> [INFO] Wrote Eclipse project for "tutorial1" to
/Users/Howard/Documents/workspace/tutorial1.
>> [INFO]
>> [INFO]
------------------------------------------------------------------------
>> [INFO] BUILD SUCCESSFUL
>> [INFO]
------------------------------------------------------------------------
>> [INFO] Total time: 5 seconds
>> [INFO] Finished at: Wed Nov 17 17:13:11 PST 2010
>> [INFO] Final Memory: 21M/81M
>> [INFO]
------------------------------------------------------------------------
>> ~/Documents/workspace/tutorial1
>> $
>>
>> At this point, Maven has created the Eclipse .project and
.classpathfiles, and we can import the project.
>>
>> Launch Eclipse and switch over to the Java Perspective.
>>
>> Right click inside the Package Explorer view and select *Import ...*
>>
>>
>> <
https://cwiki.apache.org/confluence/download/attachments/23340356/eclipse-java-persp.png
>
>>
>> Choose the "existing projects" option:
>>
>>
>> <
https://cwiki.apache.org/confluence/download/attachments/23340356/eclipse-import.png
>
>>
>> Now select the folder created by Maven:
>>
>>
>> <
https://cwiki.apache.org/confluence/download/attachments/23340356/eclipse-import-folder.png
>
>>
>> When you click the Finish button, the project will be imported into the
>> Eclipse workspace.
>>
>>
>> <
https://cwiki.apache.org/confluence/download/attachments/23340356/eclipse-project-errors.png
>
>>
>> However; there are many errors. Maven expects that you will configure a
>> classpath variable, M2_REPO, that points at your local repository; a
>> directory in your home directory that stores all those downloaded JARs
and
>> other files. Open Eclipse's preferences panel and navigate to *Java >
>> Build Path > Classpath Variables*:
>>
>>
>> <
https://cwiki.apache.org/confluence/download/attachments/23340356/eclipse-classpath-vars.png
>
>>
>> Click the *New* button, and enter the new variable (you'll have to adjust
>> this for your operating system and local paths):
>>
>>
>> <
https://cwiki.apache.org/confluence/download/attachments/23340356/eclipse-new-var.png
>
>>
>> Eclipse will ask to perform a clean build, and the errors will be gone
once
>> it has done so.
>> Investigating the Generated Artifacts
>>
>> Maven dictates the layout of the project:
>>
>> - 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 under src/main/resources and src/test/resources
>>
>> Tapestry uses a number of non-code resources, such as template files and
>> message catalogs, which will ultimately be packaged into the WAR file
>> alongside the Java classes.
>>
>> Let's look at what the archetype has created for us, starting with the
>> web.xml configuration file:
>> *src/main/webapp/WEB-INF/web.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.tutorial</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 5 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.
>>
>> 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
>>
>> The final piece of the puzzle is the Java class for the page. Tapestry
has
>> very specific rules for where page classes go. Remember the package name
>> (configured inside web.xml)? Tapestry adds a sub-package, "pages", to it
and
>> the Java class goes there. Thus the full Java class name is
>> org.apache.tapestry5.tutorial.pages.Index.
>> *src/main/java/com/example/tutorial/pages/Index.java*
>>
>> package org.apache.tapestry5.tutorial.pages;
>> import java.util.Date;
>>
>> /**
>> * Start page of application tutorial1.
>> */public class Index
>> {
>> public Date getCurrentTime()
>> {
>> return new Date();
>> }
>> }
>>
>> That's pretty darn simple: No classes to extend, no interfaces to
>> implement, just a very pure POJO (Plain Old Java Object). You do have to
>> meet the Tapestry framework halfway:
>>
>> - You need to put the Java class in the expected package,
>> org.apache.tapestry5.tutorial.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)
>>
>> As we saw when running the application, this page displays the current
date
>> and time. the currentTime property is where that value comes from;
shortly
>> we'll see how that value is 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 Model-View-Controller pattern<
http://en.wikipedia.org/wiki/Model_view_controller>(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 POJO Java class with a Tapestry
>> component template. The 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/resource/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.
>>
>> 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.
>> 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.
>>
>> For the most part, the template looks like ordinary XHTML:
>> *src/main/resources/com/example/tutorial/pages/Index.tml*
>>
>> <html t:type="layout" title="tutorial1 Index"
>> t:sidebarTitle="Current Time"
>> xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd"
>> xmlns:p="tapestry:parameter">
>> <!-- Most of the page content, including <head>, <body>, etc. tags, comes
from Layout.tml -->
>>
>> <p>${message:greeting}</p>
>>
>> <p:sidebar>
>>
>> <p>
>> Just to prove this is live:
>> </p>
>>
>> <p>The current time is: ${currentTime}.</p>
>>
>>
>> <p>
>> [<t:pagelink page="Index">refresh</t:pagelink>]
>> </p>
>> </p:sidebar>
>> </html>
>>
>> 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 Windows) and not on others
>> (Mac OS X, 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 2
>> <https://cwiki.apache.org/confluence#Footnote2> . 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.
>>
>> This short template demonstrates quite a few features of Tapestry.
>> Part of the concept of the quickstart archetype is to demonstrate a bunch
>> of different features, approaches and common patterns used in Tapestry,
thus
>> we're hitting you with a lot all at once.
>>
>> First of all, there are two XML namespaces defined:
>>
>> xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd"
>> xmlns:p="tapestry:parameter"
>>
>> The first namespace, "t:", it 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).
>>
>> The second namespace, "p:", is a way of marking a chunk of the template
as
>> a parameter passed into another component. We'll expand on that shortly.
>>
>> A Tapestry 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:
>>
>> <p>The current time is: ${currentTime}</p>
>>
>> If you are coming to Tapestry 5 from Tapestry 4 or earlier, expansions
>> are a concise replacement for the Insert component.
>>
>> The value inside the curly braces is a *property expression*. Tapestry
>> uses its own property expression language that is expressive, fast, and
>> type-safe 3 <https://cwiki.apache.org/confluence#Footnote3> . 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 4
>> <https://cwiki.apache.org/confluence#Footnote4> .
>>
>> 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
concert
>> 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:
>>
>> <p>${message:greeting}</p>
>>
>> Here greeting is not a property of the page; its actually a localized
>> message key. Every page and component is allowed to have its own message
>> catalog.
>> *src/main/resources/com/example/tutorial/pages/Index.properties*
>>
>> 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 later). 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.
>>
>> 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 5
>> <https://cwiki.apache.org/confluence#Footnote5> :
>>
>> - As an ordinary element, but with a t:type attribute to define the
>> type of component.
>>
>>
>> - 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.
>>
>> <html t:type="layout">
>> ...</html>
>>
>> But for the PageLink component, we've used an element in the Tapestry
>> namespace:
>>
>> <t:pagelink> ... </t:pagelink>
>>
>> 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 "pagelink")
>> were in all lower case; the actual class names are Layout and PageLink.
>> 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
>> "pagelink" is mapped to Tapestry's built-in
>> org.apache.tapestry5.corelib.components.PageLink 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 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 unecessary.
>>
>> <html t:type="layout" title="tutorial1 Index"
>> t:sidebarTitle="Current Time"
>>
>> This binds two parameters, title and sidebarTitle of the Layout
>> component, to the literal strings "tutorial1 Index" and "Current Time",
>> 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 bit. The point is,
the
>> page's template is integrated into the Layout components. The following
>> diagram shows how parameters passed to the Layout component end up
rendered
>> in the final page:
>> [image: A Gliffy Diagram named: Templates and Parameters]
>>
>> 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.
>>
>> <t:pagelink page="Index">refresh</t:pagelink>
>>
>> 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.
>> Running the Application inside Eclipse
>>
>> In the next chapter, we'll start to build a simple hi-lo guessing game,
but
>> we've got one more task before then, plus a magic trick.
>>
>> The task is to set up Jetty to run our application directly out of our
>> Eclipse workspace. This is a great way to develop web applications, since
we
>> don't want to have to use Maven to compile and run the application ... or
>> worse yet, use Maven to package and deploy the application. That's for
>> later, when we want to put the application into production. For
development,
>> we want a fast, agile environment that can keep up with our changes, and
>> that means we can't wait for redeploys and restarts.
>>
>> Choose the *Run ...* item from the Eclipse *Run* menu to get the launch
>> configuration dialog:
>>
>>
>> <
https://cwiki.apache.org/confluence/download/attachments/23340356/eclipse-run.png
>
>>
>> Select *Jetty Webapp* and click the *New* button, then fill in a few
>> values:
>>
>>
>> <
https://cwiki.apache.org/confluence/download/attachments/23340356/eclipse-launch.png
>
>>
>> Make sure you clear the field labeled *HTTPS*.
>>
>> You can then click *Run* and Jetty will launch (it takes only a few
>> seconds):
>>
>> Once you click Run, Jetty will start up and launch (it should take about
>> two seconds).
>>
>>
>> <
https://cwiki.apache.org/confluence/download/attachments/23340356/eclipse-jetty.png
>
>>
>> You may now start the application with the URL
>> http://localhost:8080/tutorial1/.
>> A Magic Trick
>>
>> Now it's time for the magic trick. Edit Index.java and change the
>> getCurrentTime() method to:
>> *Index.java (partial)*
>>
>> public String getCurrentTime()
>> {
>> return "A great day to learn Tapestry";
>> }
>>
>> Make sure you save changes; then click the refresh link in the web
>> browser:
>>
>>
>> <
https://cwiki.apache.org/confluence/download/attachments/23340356/app-live-reload.png
>
>>
>> This is one of Tapestry's early *wow factor* features: changes to your
>> component classes are picked up immediately. 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.
>>
>> 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:
>>
>>
>> <
https://cwiki.apache.org/confluence/download/attachments/23340356/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:
>>
>>
>> <
https://cwiki.apache.org/confluence/download/attachments/23340356/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.
>>
>> Tapestry displays the stack trace of the deepest exception, along with
lots
>> of details about the runtime 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.
>> 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.
>>
>> Now that we have our basic application set up, and ready to run (or
debug)
>> directly inside Eclipse, we can start working on implementing our Hi/Lo
game
>> in earnest.
>> ------------------------------
>>
>> Footnotes Reference Notes 1
>> <https://cwiki.apache.org/confluence#FootnoteMarker1> Yes, I'm on a Mac.
>> Get one. 2 <https://cwiki.apache.org/confluence#FootnoteMarker2> By
>> static, we mean unchanging, as opposed to a dynamically generated
Tapestry
>> page. 3 <https://cwiki.apache.org/confluence#FootnoteMarker3> Tapestry
>> does *not* use reflection to implement property expressions. 4
>> <https://cwiki.apache.org/confluence#FootnoteMarker4> 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. 5
>> <https://cwiki.apache.org/confluence#FootnoteMarker5> Ok, there's a third
>> way as well, which will be discussed in good time.
>>
>> Continue on to chapter 3: Implementing The Hi/Lo Game<
https://cwiki.apache.org/confluence/display/TAPESTRY/Hilo>
>> Change Notification Preferences<
https://cwiki.apache.org/confluence/users/viewnotifications.action>
>> View Online <https://cwiki.apache.org/confluence/display/TAPESTRY/First>
|
>> View Changes<
https://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=23340356&revisedVersion=13&originalVersion=12
>
>>