Thanks! Let me put it this way ... this tutorial is going to cost me $1K - $2K in lost billable time.
On Thu, Nov 18, 2010 at 5:42 PM, Josh Canfield <[email protected]>wrote: > 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 > > > >> > -- Howard M. Lewis Ship Creator of Apache Tapestry The source for Tapestry training, mentoring and support. Contact me to learn how I can get you up and productive in Tapestry fast! (971) 678-5210 http://howardlewisship.com
