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> >
