blob: fe1831a8328185d87b655a196b07b055a7f3b309 [file] [log] [blame]
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2005 The Apache Software Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<document>
<properties>
<title>Quick Start: Hello World</title>
</properties>
<body>
<section name="Quick Start: Hello World">
<p>
In this tutorial, we'll cover setting up the most basic Tapestry application, a simple
"Hello World" application that displays the current time. We'll then extend it just a
bit, adding a touch of interactivity.
</p>
<p>
The final source for this tutorial is packaged as
<strong>helloworld.tar.gz</strong>
.
</p>
<section name="Tapestry Application Basics">
<p>Our first application will look like the following when it runs:</p>
<img src="../images/QuickStart/helloworld1.png" alt="HelloWorld step 1 screen shot." />
<p>
Tapestry applications always include a page named "Home". The Home page is the first
page displayed by the application, when it is first started (that is, when the
client web browser first accesses the starting URL).
</p>
<p>
Tapestry pages are always a combination of a Java class and a template (we could
say, "an HTML template", but Tapestry is not limited to just HTML). In many cases,
Tapestry will use a built-in Java class when you don't provide one; for a trivial
page like ours, we don't need to supply a Java class at all.
</p>
<p>
We'll start with the HTML template then, which is a file named Home.html in the root
of the web application context. In the project, it is stored as
src/context/Home.html:
</p>
<source xml:space="preserve">
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Tutorial: HelloWorld&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;HelloWorld Tutorial&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;
</source>
<p>
There's nothing special in this HTML template, nothing dynamic (not yet, anyway). We
could access it as http://localhost:8080/helloworld/Home.html and see the same
thing; but notice that in the screen shot the URL is
<a href="http://localhost:8080/hellworld/app">
http://localhost:8080/hellworld/app
</a>
. That means that a servlet, mapped to the /app path within the web application, was
responsible for the output you can see in the web browser.
</p>
<p>
Tapestry applications always use a specific servlet class provided with the
framework. This is defined in the web deployment descriptor, web.xml. This file is
stored in the project as src/context/WEB-INF/web.xml:
</p>
<source xml:space="preserve">
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd"&gt;
&lt;web-app&gt;
&lt;display-name&gt;Tutorial: HelloWorld&lt;/display-name&gt;
&lt;servlet&gt;
&lt;servlet-name&gt;app&lt;/servlet-name&gt;
&lt;servlet-class&gt;org.apache.tapestry.ApplicationServlet&lt;/servlet-class&gt;
&lt;load-on-startup&gt;0&lt;/load-on-startup&gt;
&lt;/servlet&gt;
&lt;servlet-mapping&gt;
&lt;servlet-name&gt;app&lt;/servlet-name&gt;
&lt;url-pattern&gt;/app&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;
&lt;/web-app&gt;
</source>
<p>
Here we've given our application a name, "app". We're using the standard Tapestry
ApplicationServlet class as our servlet, and mapped it to the path /app. The name
you choose for you application is relatively unimportant, and Tapestry will adapt to
whatever name you do choose.
</p>
<p>
The path, on the other hand, needs to be /app. This is not hard-coded into Tapestry,
but does require a small amount of configuration if you choose to use another path.
As well see in a later tutorial, Tapestry can be quite sophisticated in terms of
building application URLs, so don't be too concerned about this aspect of Tapestry
just yet.
</p>
<p>
That's all there is to HelloWorld in this phase. No Java code at all. Before going
on to bigger and better things, we're going to add a little bit to this application,
to give you a slightly more realistic feel for Tapestry ... but we'll still avoid
writing any Java code at all.
</p>
</section>
<section name="Adding dynamic output">
<p>
We're going to change the Home page to display the current date and time. It will
look something like this:
</p>
<img src="../images/QuickStart/helloworld2.png"
alt="HelloWorld showing current date/time" />
<p>
In Tapestry, pretty much any time anything dynamic occurs, there's going to be a
<em>component</em>
involved. Tapestry components are much like Tapestry pages ... they consist of a
template and a Java class (purists may note that there may be an XML file to tie
those together, and that the template and the Java class are both optional -- more
on this later).
</p>
<p>
Tapestry components "hide" inside the HTML template. They look like ordinary HTML
tags, but have extra attributes in them, often with unusual values. The revised
Home.html template:
</p>
<source xml:space="preserve">
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Tutorial: HelloWorld&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;HelloWorld Tutorial&lt;/h1&gt;
&lt;p&gt;
The current data and time is:
&lt;strong&gt;&lt;span jwcid="@Insert" value="ognl:new java.util.Date()"&gt;June 26 2005&lt;/span&gt;&lt;/strong&gt;
&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
</source>
<p>
The &lt;span&gt; tag is the placeholder within the template for the component. The
special attribute, jwcid, is Tapestry's clue that this is a component, and not just
ordinary HTML.
</p>
<p>
The "@Insert" value for the jwcid attribute can be thought of as "instance of the
<a href="../components/general/insert.html">Insert</a>
component". Insert is one of many built-in Tapestry components. This isn't quite the
Java class; it is a component type, which is used by Tapestry to find out about the
component, such as what parameters can be configured and what Java class contains
the logic for the component. Again, more on that later.
</p>
<p>
Before we get to the value parameter, a word about the
<em>body</em>
of the component. The body is the portion of the template enclosed by the
component's start (&lt;span&gt;) and end (&lt;/span&gt;) tags. Ultimately, the
component itself determines when, if, or how many times it will render its body
("render" is the verb used throughout Tapestry meaning "write HTML output").
</p>
<p>
The Insert component expressly
<em>does not</em>
render its body. Any text inside the component's body is quietly discarded at
runtime. We could, in fact, abbreviate the component within the template to just
<code>&lt;span jwcid="@Insert" value="ognl:new java.util.Date()"/&gt;</code>
and not put any text inside its tags. So why did we?
</p>
<p>
The answer is
<em>previewability</em>
, that is, the ability to see, at least approximately, what the page will look like
<em>without</em>
running the actual application. You can load the Home.html file into a web browser,
or a specialized editor such as Dreamweaver or HomeSite, and see what it looks like.
For example, if we bypass the Tapestry servlet and access the template directly, we
see the following:
</p>
<img src="../images/QuickStart/helloworld3.png" alt="HelloWorld Home.html template" />
<p>
That
<em>provisional text</em>
, "June 26 2005" is not
<em>exactly</em>
what the application will display at runtime ... but it's close enough; it's not
blank and it's approximately right. In a real application with real style sheets and
layouts, this would be enough validate that the layout of the running application
was correct.
</p>
<span class="info">
<strong>Note:</strong>
<p>
This side track, about previewability, is actually one of the cornerstones of
Tapestry: a
<em>clean</em>
seperation between logic and content. A non-Java HTML developer could edit this
template and make significant changes and validate them in their editor of choice
without involving a Java developer. As long as the HTML side of the team honors the
components, the tags with a jwcid attribute, and doesn't make changes to
<em>those</em>
elements, the rest of the HTML template can be freely editted. It is only at the
junction between content and runtime behavior, that is,
<em>inside</em>
components, that HTML and Java developers need to work together.
</p>
</span>
<p>
So, what does
<code>value="ognl:new java.util.Date()"</code>
mean? Let's start with the attribute value,
<code>ognl:new java.util.Date</code>
. The "ognl:" prefix signals to Tapestry that this is an expression to be evaluated,
rather than a ordinary, literal string. If we did want the Insert to always render
the same literal string, such as "Tapestry Rocks!", we wouldn't need the prefix, we
could just write
<code>value="Tapestry Rocks!"</code>
.
</p>
<p>
OGNL is the Object Graph Navigation Language, an open source expression language
used by several open-source projects, including Tapestry,
<a href="http://opensymphony.com/webwork/">WebWork</a>
and
<a href="http://springframework.org/">Spring</a>
. OGNL has some astounding capabilities, not just reading and updating object
properties, but also includes support for creating new objects entirely (as here),
as well as creating lists, maps and arrays of objects.
</p>
<p>
Here, evaluating the expression results in a new instance of the java.util.Date
class. This Date instance is
<em>bound</em>
to the value parameter of the Insert component. "Bound" is another specialized
Tapestry term, one that concerns the relationship between a component parameter and
a property (or expression) of its container. Here, the component is the Insert
component, the container is the Home page, and the expression is
<code>new java.util.Date</code>
. Binding might look like just an assignment of a property of the Insert component,
but is a bit more; components often use bindings to
<em>update</em>
properties of their container, something we'll see when discussing the form element
components.
</p>
<p>
In the Java code for the Insert component is the logic that obtains value parameter
and converts it into into a string that is rendered into the response.
</p>
<p>
So, the expression provides the Date instance, the value parameter gives the Insert
component access to that value, and the Insert component provides the logic for
converting that Date into a string and having it show up on the rendered page. Every
time the Insert component renders, it will re-read its value parameter, causing the
expression to be evaluated once more, and a new Date instance to be created. You can
see this by hitting your browser's refresh button repeatedly; the displayed date
will keep changing.
</p>
<p>
In the next section, we'll see how to create a link to get the displayed date to be
updated.
</p>
</section>
<section name="Creating Links">
<p>
We're going to extend the application once more, adding a refresh link that we can
click instead of using the browser's refresh button. The end result looks like:
</p>
<img src="../images/QuickStart/helloworld4.png" alt="HelloWorld with refresh link" />
<p>We created this new link by adding the following to Home.html:</p>
<source xml:space="preserve">
&lt;p&gt;
&lt;a href="#" jwcid="@PageLink" page="Home"&gt;refresh&lt;/a&gt;
&lt;/p&gt;
</source>
<p>
Again, anything dynamic in Tapestry is going to involve a component; here it's the
<a href="../components/link/pagelink.html">PageLink</a>
component, one of a family of components that generate callback links into a
Tapestry application.
</p>
<p>
Tapestry automatically creates a URL for this; you can see this URL in the
screenshot: http://localhost:8080/workbench/app?page=Home&amp;service=page. That URL
provides two critical pieces of information: service=page means "render a page", and
page=Home identifies which page to render.
</p>
<p>
<em>Do I really need a component for that?</em>
You might be tempted to change the template to:
</p>
<source xml:space="preserve">
&lt;a href="/app?service=page&amp;amp;page=Home"&gt;refresh&lt;/a&gt;
</source>
<p>
This is a
<strong>bad idea</strong>
. Tapestry is doing more than spewing out a URL, it's
<em>session encoding</em>
the URL for you
<sup>1</sup>
and may be doing other useful things that we'll see later. Further, second guessing
Tapestry's URLs is never a good idea; the PageLink component will be around in the
next release of Tapestry, but the URL format may change between releases.
</p>
<p>
Another question: what's with the href attribute in the attribute? What does
<code>href="#"</code>
mean? This is another side to previewability: to preview this page, the link
ultimately generated by the PageLink needs to preview as a
<em>link</em>
. An &lt;a&gt; tag without an href attribute is an
<em>anchor</em>
. Again, this is a distinction that is more visible in real applications, supported
by a style sheet.
</p>
<p>
The href provided in the HTML template is simply discarded in favor of the href
attribute generated inside the PageLink components (/app?service=...). You can
actually mix and match these component-generated attributes with extra attributes
provided in the template; these are called
<em>informal parameters</em>
, and are covered in a later tutorial.
</p>
</section>
<section name="Next: DirectLink">
<p>
That covers the PageLink component, at least for now. Next up: the
<a href="directlink.html">DirectLink Tutorial</a>
.
</p>
</section>
<span class="info">
<strong>Note:</strong>
<p>
<sup>1</sup>
Session encoding is an aspect of the Servlet API. Encoding a URL adds information about
the server-side session (the HttpSession) if there is one. Although this information can
be obtained via HTTP cookies, not all users have cookies enabled in their browser. The
servlet specification encourages you to always encode your URLs, and this is simply done
for you in Tapestry. It's another example of the basic Tapestry principle:
<em>make the simplest choice the correct choice</em>
.
</p>
</span>
</section>
</body>
</document>