Frequently Asked QuestionsPage edited by Howard M. Lewis ShipChanges (2)
Full Content
General QuestionsHow do I get started with Tapestry?The easiest way to get started is to use Apache Maven to create your initial project; Maven can use an archetype (a kind of project template) to create a bare-bones Tapestry application for you. One you have Maven installed, execute the command mvn archetype:generate -DarchetypeCatalog=http://tapestry.apache.org. Maven will (after performing a large number of one-time downloads) ask you questions about how to create the new project, including a group id (like a package name) and an artifact id for your new project. Once it is created, you can load it into any IDE and start coding, or use mvn jetty:run 1 . Again, more one-time downloads, but then you can open your browser to http://localhost:8080 to run the application. Why does Tapestry use Prototype? Why not insert favorite _javascript_ library here?An important goal for Tapestry is seamless DHTML and Ajax integration. To serve that goal, it was important that the built in components At the time (this would be 2006-ish), Prototype and Scriptaculous were well known and well documented, and jQuery was just getting started. The intent has always been to make this aspect of Tapestry pluggable. This is work expected in Tapestry 5.3, where a kind of adapter layer will be introduced so that Tapestry can be easily customized to work with any of the major _javascript_ libraries. Why does Tapestry have its own Inversion of Control Container? Why not Spring or Guice?An Inversion of Control Container is the key piece of Tapestry's infrastructure. It is absolutely necessary to create software as robust, performant and extensible as Tapestry. Tapestry IoC includes a number of features that distinguish itself from other containers:
Because Tapestry is implemented on top of its IoC container, and because the container makes it easy to extend or replace any service inside the container, it is possible to make the small changes to Tapestry needed to customize it to any project's needs. Ajax ComponentsDo I have to specify both id and t:id for Zone components?The examples for the Zone component (in the Component Reference) consistently specify both id and t:id and this is probably a good idea. Generally speaking, if you don't specify the client-side id (the id attribute), it will be the same as the Tapestry component id (t:id). However, there are any number of exceptions to this rule. The Zone may be rendering inside a Loop (in which case, each rendering will have a unique client side id). The Zone may be rendering as part of a partial page render, in which case, a random unique id is inserted into the id. There are other examples where Tapestry component ids in nested components may also clash. The point is, to be sure, specify the exact client id. This will be the value for the zone parameter of the triggering component (such as a Form, PageLink, ActionLink, etc.). Integration with existing applicationsYou may have an existing JSP (or Struts, Spring MVC, etc.) application that you want to migrate to Tapestry. It's quite common to do this in stages, moving some functionality into Tapestry and leaving other parts, initially, in the other system. How do I make a form on a JSP submit into Tapestry?Tapestry's Form component does a lot of work while an HTML form is rendering to store all the information needed to handle the form submission in a later request; this is all very specific to Tapestry and the particular construction of your pages and forms; it can't be reproduced from a JSP. Fortunately, that isn't necessary: you can have a standard HTML Form submit to a Tapestry page, you just don't get to use all of Tapestry's built in conversion and validation logic. All you need to know is how Tapestry converts page class names to page names (that appear in the URL). It's basically a matter of stripping off the root-package.pages prefix from the fully qualified class name. So, for example, if you are building a login screen as a JSP, you might want to have a Tapestry page to receive the user name and password. Let's assume the Tapestry page class is com.example.myapp.pages.LoginForm; the page name will be loginform 1 , and the URL will be /loginform.
<form method="post" action="" class="code-quote">"/loginform"> <input type="text" value="userName"/> <br/> <input type="password" value="password"/> <br/> <input type="submit" value="Login"/> </form> On the Tapestry side, we can expect that the LoginForm page will be activated; this means that its activate event handler will be invoked. We can leverage this, and Tapestry's RequestParameter annotation: public class LoginForm { void onActivate(@RequestParameter("userName") String userName, @RequestParameter("password") String password) { // Validate and store credentials, etc. } } The RequestParameter annotation extracts the named query parameter from the request, coerces its type from String to the parameter type (here, also String) and passes it into the method. How do I share information between a JSP application and the Tapestry application?From the servlet container's point of view, there's no difference between a servlet, a JSP, and an entire Tapestry application. They all share the same ServletContext, and (once created), the same HttpSession. On the Tapestry side, it is very easy to read and write session attributes: public class ShowSearchResults { @SessionAttribute private SearchResults searchResults; } Reading the instance variable searchResults is instrumented to instead read the corresponding HttpSession attribute named "searchResults". You can also specify the value attribute of the SessionAttribute annotation to override the default attribute name. Writing to the field writes into the session. The session is automatically created as needed. LimitationsHow do I add new components to an existing page dynamically?The short answer here is: you don't. The long answer here is you don't have to, to get the behavior you desire. One of Tapestry basic values is high scalability: this is expressed in a number of ways, reflecting scalability concerns within a single server, and within a cluster of servers. Although you code Tapestry pages and components as if they were ordinary POJOs 1 , as deployed by Tapestry they are closer to a traditional servlet: a single instance of each page services requests from multiple threads. Behind the scenes, Tapestry transforms you code, rewriting it on the fly. What this means is that any incoming request must be handled by a single page instance. Therefore, Tapestry enforces the concept of static structure, dynamic behavior. Tapestry provides quite a number of ways to vary what content is rendered, well beyond simple conditionals and loops. It is possible to "drag in" components from other pages when rendering a page (other FAQs will expand on this concept). The point is, that although a Tapestry page's structure is very rigid, the order in which the components of the page render does not have to be top to bottom. Why doesn't my service implementation reload when I change it?Live service reloading has some limitations; basically, the implementation only reloads if Tapestry instantiated it directly. Consider the following example module: public static void bind(ServiceBinder binder) { binder.bind(ArchiveService.class, ArchiveServiceImpl.class); } public static JobQueue buildJobQueue(MessageService messageService, Map<String,Job> configuration) { JobQueueImpl service = new JobQueueImpl(configuration); messageService.addQueueListener(service); return service; } ArchiveService is reloadable, because Tapestry instantiates ArchiveServiceImpl itself. On the other hand, Tapestry invokes buildJobQueue() and it is your code inside the method that instantiates JobQueueImpl, so the JobQueue service will not be reloadable. Finally, only classes whose class files are stored directly on the file system, and not packaged inside JARs, are ever reloadable ... generally, only the services of the application being built (and not services from libraries) will be stored on the file system. This reflects the intent of reloading: as an agile development tool, but not something to be used in deployment. Page And Component ClassesWhat's the difference between a page and a component?There's very little difference between the two. Pages clases must be in the root-package.pages package; components must be in the Other than that, they are more equal than they are different. They may have templates or may render themselves in code (pages usually have a template, components are more likely to render only in code). The major difference is that Tapestry page templates may be stored in the web context directory, as if they were static files (they can't be accessed from the client however; a specific rule prevents access to files with the .tml extension).
How do I store my page classes in a different package?Tapestry is very rigid here; you can't. Page classes must go in root-package.pages, component classes in root-package.components, etc. Why do my instance variables have to be private?Tapestry does a large amount of transformation to your simple POJO classes as it loads them into memory. In many cases, it must locate every read or write of an instance variable and change its behavior; for example, reading a field that is a component parameter will Limiting fields to private means that Tapestry can do the necessary processing one class at a time, as needed, at runtime. More complex Why don't my informal parameters show up in the rendered markup?Getting informal parameters to work is in two steps. First, you must make a call to the ComponentResources.renderInformalParameters() method, but just as importantly, you must tell Tapestry that you want the component to support @SupportsInformalParameters public class DBImage { @Parameter(required=true) private Image image; @Inject private ComponentResources resources; boolean beginRender(MarkupWriter writer) { writer.element("img", "src", image.toClientURL(), "class", "db-image"); resources.renderInformalParameters(writer); writer.end(); return false; } } Why do I get java.lang.LinkageError when I invoke public methods of my page classes?In Tapestry, there are always two versions of page (or component) classes. The first version is the version loaded by standard class loader: the simple POJO version that you wrote. The second version is much more complicated; it's the transformed version of your code, with lots of extra hooks and changes to allow the class to operate inside Tapestry. This includes implementing Although these two classes have the same fully qualified class name, they are distinct classes because they are loaded by different class loaders.
In a Tapestry application, most application classes, including your services, are loaded from the middle class loader. When a page or component is passed as a parameter to a service, a failure occurs The solution is to define an interface with the methods that the service will invoke on the page or component instance. The service will expect an object implementing the interface (and doesn't care what class loader loaded Just be sure to put the interface class in a non-controlled package, such as you root-package (and not root-package.pages). Specific ErrorsWhy do I get the exception "No service implements the interface org.apache.tapestry5.internal.InternalComponentResources" when trying to use the BeanEditForm component?This can occur when you choose the wrong package for your data object, the object edited by the BeanEditForm component. If you place it in the same package as your pages, Tapestry will treat it like a page, and perform a number of transformation on it, including adding a new constructor. Only component classes should go in the Tapestry-controlled packages (pages, components, mixins and base under your application's root package). By convention, simple data objects should go in a data package, and Hibernate entities should go in an entities package. Tapestry Inversion of Control ContainerWhy do I need to define an interface for my services? Why can't I just use the class itself?First of all: you can do exactly this, but you lose some of the functionality that Tapestry's IoC container provides. The reason for the split is so that Tapestry can provide functionality for your service around the core service implementation. It does this by creating proxies: Java classes One of the primary purposes for proxies is to encapsulate the service's lifecycle: most services are singletons that are created just in time. Just in time means only as soon If you binding a service class (not a service interface and class), then the service is fully instantiated the first time it is injected, rather than at that first method invocation. Further, you The final reason for the service interface / implementation split is to nudge you towards always coding to an interface, which has manifest benefits for code structure, robustness, and testability. Templating and MarkupWhy do I get a SAXParseException when I use an HTML entity, such as in my template?Tapestry uses a standard SAX parser to read your templates. This means that your templates must be well formed: open and close tags must balance, attribute values must be quoted, and entities must be declared. The easiest way to accomplish this is to add a DOCTYPE to your the top of your template: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> Part of the DOCTYPE is the declaration of entities such as . Alternately, you can simply use the numeric version:   This is the exact same character and will render identically in the browser. Why do some images in my page show up as broken links?You have to be careful when using relative URLs inside page templates; the base URL may not always be what you expect. For example, inside your ViewUser.tml file, you may have:
<img class="icon" src="" class="code-quote">"icons/admin.png"/>${user.name} has Administrative access
This makes sense; ViewUser.tml is in the web context, as is the icons folder. The default URL for this page will be /viewuser 1 However, most likely, the ViewUser page has a page activation context to identify which user is to be displayed: public class ViewUser @Property @PageActivationContext private User user; . . . With a page activation context, the URL for the page will incorporate the id of the User object, something like /viewuser/37371. This is why the relative URLs to the admin.png image is broken: the base path is relative to the page's URL, not to the page template 2 . One solution would be to predict what the page URL will be, and adjust the path for that:
<img class="icon" src="" class="code-quote">"../icons/admin.png"/>${user.name} has Administrative access
But this has its own problems; the page activation context may vary in length at different times, or the template in question may be a component used across many different pages, making it difficult to predict what the correct relative URL would be. The best solution for this situation, one that will be sure to work in all pages and all components, is to make use of the context: binding prefix:
<img class="icon" src="" class="code-quote">"${context:icons/admin.png}"/>${user.name} has Administrative access
The src attribute of the <img> tag will now be bound to a dynamically computed value: the location of the image file relative to the What's the difference between id and t:id?You might occasionally see something like the following in a template:
<t:zone id="status" t:id="statusZone">
Why two ids? Why are they different? The t:id attribute is the Tapestry component id. This id is unique within its immediate container. This is the id you might use
@InjectComponent
private Zone statusZone;
The other id is the client id, a unique id for the rendered element within the client-side DOM. _javascript_ that needs to access the element uses this id. For example: $('status').hide(); In many components, the id attribute is an informal parameter; a value from the template that is blindly echoed into the output document. In other cases, the component itself has an id attribute. Often, in the latter case, the Tapestry component id is the default value for the client id.
—
Change Notification Preferences
View Online
|
View Changes
|
- [CONF] Apache Tapestry > Frequently Asked Questions confluence
- [CONF] Apache Tapestry > Frequently Asked Questions confluence
- [CONF] Apache Tapestry > Frequently Asked Questions confluence
- [CONF] Apache Tapestry > Frequently Asked Questions confluence
- [CONF] Apache Tapestry > Frequently Asked Questions confluence
- [CONF] Apache Tapestry > Frequently Asked Questions confluence
- [CONF] Apache Tapestry > Frequently Asked Questions confluence
- [CONF] Apache Tapestry > Frequently Asked Questions confluence
- [CONF] Apache Tapestry > Frequently Asked Questions confluence
- [CONF] Apache Tapestry > Frequently Asked Questions confluence
- [CONF] Apache Tapestry > Frequently Asked Questions confluence
- [CONF] Apache Tapestry > Frequently Asked Questions confluence
- [CONF] Apache Tapestry > Frequently Asked Questions confluence
- [CONF] Apache Tapestry > Frequently Asked Questions confluence
- [CONF] Apache Tapestry > Frequently Asked Questions confluence