[jira] Commented: (TAP5-627) Allow injection of named spring beans
[ https://issues.apache.org/jira/browse/TAP5-627?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanelfocusedCommentId=12879292#action_12879292 ] Baptiste Autin commented on TAP5-627: - +1 Several beans of the same type in the same container is a frequent situation (for instance when you have a DAO class that inherits from another one) IMHO, injecting the ApplicationContext and calling getBean() is not a reasonable option. For good reasons, Spring users dislike injecting the ApplicationContext, and so supporting named spring beans would be *really* great. (and instead of inventing a new annotation to do this, why not supporting the JSR 250 @Resource ?) Allow injection of named spring beans - Key: TAP5-627 URL: https://issues.apache.org/jira/browse/TAP5-627 Project: Tapestry 5 Issue Type: New Feature Components: tapestry-spring Affects Versions: 5.1.0.2 Reporter: Paul Field Since 5.1 now handles Spring beans through the master object provider, it is (I think) impossible to specify spring beans by name, which is a pain when you have multiple spring beans implementing the same interface. Could the Spring Object Provider take notice of an annotation that specifies the bean name, please? (e.g. @Service or @Id) It would be particularly nice if the same annotation specified the names of Tapestry IOC services *and* spring beans, so that if we decide to move services from Spring into Tapestry IOC we don't need to change any other code. -- This message is automatically generated by JIRA. - You can reply to this email to add a comment to the issue online.
[CONF] Apache Tapestry Content Type and Markup
Content Type and Markup Page added by Ulrich Stärk Content Type and Markup Output Tapestry reads well-formed XML template files and renders its output as XML, with minor caveats: The ?xml? XML declaration is omitted. Most element render with an open and close tag, even if empty. Certain elements will be abbreviated to just the open tag, if empty: br hr img ![CDATA[] sections are not used This is all to ensure that the markup stream, while (almost) well formed, is still properly understood by browsers expecting ordinary HTML. In fact, Tapestry may decide to render a purely XML document; it depends on the content type of the response. The default content type for pages is "text/html" ... this triggers specialized XML rendering. A page may declare its content type using the ContentType|../apidocs/org/apache/tapestry5/annotations/ContentType.html class annotation. Content types other than "text/html" will render as well-formed XML documents, including the XML declaration, and more standard behavior for empty elements. Input/Output Character Set The character set (aka character encoding) used when writing output and when parsing requests is normally "utf-8". All pages use the same encoding, which can be set using the tapestry.charset configuration setting. Change Notification Preferences View Online
[CONF] Apache Tapestry CSS
CSS Page added by Ulrich Stärk Tapestry CSS Support Cascading Style Sheets (CSS) is an important technology, even with Tapestry. Tapestry works best when the rendered HTML is simple and semantic semantic meaning HTML that goes back to its roots, simple, straightforward, with tags used for structure and, as much as possible, details about font, color and layout delegated to CSS. Default CSS Stylesheet Tapestry includes a built-in stylesheet, default.css, is all HTML documents (documents that have an outer html element and a nested head element). The default.css stylesheet is always ordered first ... any additional stylesheets will come after. This allows Tapestry's default styles to be overridden. All the styles in the default stylesheet are prefixed with "t-" (for Tapestry). Adding your own CSS A page or component that is rendering the head tag can add a stylesheet directly in the markup. head link href="" class="code-quote">"/css/myapp.css" rel="stylesheet" type="text/css"/ . . . If you want to leverage Tapestry's localization support, you may want to make use of an expansion and the asset: binding prefix: head link href="" class="code-quote">"${context:css/myapp.css}" rel="stylesheet" type="text/css"/ . . . The "context:" prefix means that the remainder of the expansion is a path to a context asset, a resource in the web application root (src/main/webapp in your workspace). Using the IncludeStylesheet Annotation Another approach to adding a stylesheet is to include an IncludeStylesheet|../apidocs/org/apache/tapestry5/annotations/IncludeStylesheet.html annotation on your component class: @IncludeStylesheet("context:css/myapp.css") public class MyComponent { } As with included _javascript_ libraries, each stylesheet will only be added once, regardless of the number of components that include it via the annotation. Change Notification Preferences View Online
[CONF] Apache Tapestry Environmental Services
Environmental Services Page added by Ulrich Stärk Environmental Services Environmental services represent yet another, distinct form of injection. Unlike service injection (injection via a service implementation's constructor) or normal component injection (directly into component fields, via the @Inject annotation) where the injected value is always the same, with environmental services, the injected value is very late bound and dynamic. Environmental services are often a conduit of communication between an outer component and the components it encloses. An example of this is form support; the Form|../tapestry-core/ref/org/apache/tapestry5/corelib/components/Form.html component creates an environmental of type FormSupport|../apidocs/org/apache/tapestry5/services/FormSupport.html. The FormSupport interface allows enclosed components to participate in both the rendering of the Form and the Form's eventual submission. This is how control names and client-side ids are determined, how fields register callbacks so that they can process their part of the submission, and how fields hook themselves to client-side validation. Using the @Environmental annotation The Environmental|../apidocs/org/apache/tapestry5/annotations/Environmental.html annotation is used to dynamically connect to a Environmental service provided by an enclosing component. A very common Environmental is RenderSupport|../apidocs/org/apache/tapestry5/RenderSupport.html, used when generating client-side _javascript_. @Inject @Path("${tapestry.scriptaculous}/dragdrop.js") private Asset dragDropLibrary; @Environmental private RenderSupport renderSupport; void setupRender() { renderSupport.addScriptLink(dragDropLibrary); } Environmental services are, by their nature, per-thread (and therefore per-request). Accessing an environmental field causes a lookup, by type, against the Environment|../apidocs/org/apache/tapestry5/services/Environment.html service. Normally, an environmental of the specified type must be available in the Environment, or an exception is thrown when accessing the field. If the value of the Environmental annotation is false, then the environmental value is optional. Placing a value in the environment The Environment service has push() and pop() methods to put a value in the Environment, and discard it. For example, say you were building a tab-based menu system and you needed to allow an outer TabGroup component to communicate with inner Tab components, to control various aspects of presentation. The relevant information could be exposed as an interface, TabModel. public class TabGroup { @Inject private Environment environment; void beginRender() { environment.push(TabModel.class, new TabModelImpl(...)); } void afterRender() { environment.pop(TabModel.class); } } public class Tab { @Environmental private TabModel model; void beginRender(MarkupWriter writer) { ... } } Notice that when pushing a value into the Environment, you identify its type as well as the instance. Environment maintains a number of stacks, one for each type. Thus, pushing a TabModel into the environment won't disturb the RenderSupport or other environmentals already there. What's important here is that the code that pushes a environmental onto a stack should also pop it off. The enclosed class, Tab, has full access to whatever object was pushed onto the stack by the TabGroup. The reason why Environment is a stack is so that a component can, when it makes sense, easily replace or intercept access to an Environmental. Fundamental Environmentals Not all environmentals are pushed into the Environment by components. A number of environmentals are initialized as part of page rendering, even before the first component starts to render. This initialization is accomplished with MarkupRendererFilter|../apidocs/org/apache/tapestry5/services/MarkupRendererFilter.html contributions to the MarkupRenderer|../apidocs/org/apache/tapestry5/service/MarkupRenderer.html service. Accessing Environmentals in Services The Environmenal annotation only works inside components. To access an Environmental inside a service implementation, you must inject the Environment service and obtain values from it using the peek() method. If this is something that will occur frequently, it is possible to create a service implementation that is "backed" by the Environment. For example, RenderSupport is accessible as a normal injection, because a service is built for it in TapestryModule: public RenderSupport buildRenderSupport(EnvironmentalShadowBuilder builder) { return builder.build(RenderSupport.class); } The EnvironmentShadowBuilder service creates a service implementation that delegates to the proper instance in the environment. The same technique can be used for your own services and environmentals.
[CONF] Apache Tapestry DOM
DOM Page added by Ulrich Stärk Document Object Model Tapestry 5 takes a very different approach to markup generation than Tapestry 4, or most other frameworks. Tapestry 4 Approach In Tapestry 4, markup generation was based on generating a character stream. At the lowest level, the fact that the output was in a markup format such as HTML, XHTML or WML was not known. Higher levels, such as the IMarkupWriter interface (and its implementations) provide the concept of markup generation: elements, attributes, start tags and end tags. Often, components would work together to generate markup. A common example would be a Form component and the many form control components it contains (such as TextField, Checkbox, etc.). The Form could not fully render until all such enclosed components had rendered first. IMarkupWriter supported nested writers. Nested writers are a way to buffer output until needed. A Form component would render its body using a nested writer, then write out its form and input type="hidden" elements, as well as the nested, buffered body. This technique breaks down when two elements are peers, and not in a parent/child relationship. For example, the rendering of a FieldLabel component is affected by its companion TextField component. Handling these cases in Tapestry 4 required a number of kludges and special cases. Tapestry 5 Approach Tapestry 5 components render out a DOM, a Document Object Model. This is a tree of nodes representing elements, attributes and text within a document. The DOM may ultimately be operated upon in a random access manner, rather than the serial (or buffered) approach used in Tapestry 4. A new MarkupWriter|../apidocs/org/apache/tapestry5/MarkupWriter.html interface allows the majority of code to treat the generation of output as a stream. In fact, MarkupWriter is more like a cursor into the DOM tree. Once all rendering is complete, the DOM tree is streamed to the client. DOM Classes The implementation of this DOM is part of Tapestry, despite the fact that several third-party alternatives exist. This represents a desire to limit dependencies for the framework, but also the Tapestry DOM is streamlined for initial creation, and a limited amount of subsequent modification. Most DOM implemenations are more sophisticated, with greater support for querying (often using XPath) and manipulation. Once the Document object is created, you don't directly create new DOM objects; instead, each DOM object includes methods that create new sub-objects. This primarily applies to the Element class, which can be a container of text, comments and other elements. Document|../apidocs/org/apache/tapestry5/dom/Document.html The Document object represents the an entire document, which is to say, an entire response to be sent to the client. Documents will have a single root element. The newRootElement() method is used to create the root element for the document. TODO: Support for doctypes, content type, processing instructions, and top-level comments. Element|../apidocs/org/apache/tapestry5/dom/Element.html An element of the document. Elements may have attributes, and they may themselves contain other elements, as well as text and comments. The addAttribute() method adds a new attribute/value pair to the Element. If an existing attribute with the specified name already exists, then then the new value is ignored. This has implications when different pieces of code try to add attributes to an Element ... the first to add an attribute will "win" Not yet implemented are some basic methods for manipulating the DOM after it is built. Plans are to add a few methods for re-parenting DOM nodes into new elements. In addition, some searching methods may be added. MarkupWriter|../apidocs/org/apache/tapestry5/MarkupWriter.html The MarkupWriter interface allows the structure of the document to be built while maintaining a streaming metaphor. element() and end() Calls to element() create a new element within the tree, and may provide attributes for the new element as well. Calls to write(), writeln() and writef() write text nodes within the current element. Every call to element() should be matched with a call to end(), which is used to move the current node up one level. writer.element("img", "src", "icon.png", "width", 20, "height", 20, alt, "*"); writer.end(); Note that end() must be called here, even though the img element is empty (has no body). If the call to end() is omitted, then later elements created by calls to element() will be nested inside the img element, which is not desired. Again, every call to element() must be matched with a call to end(): writer.element("select", "name", "choice"); for (String name : optionsNames) { writer.element("option"); writer.write(name); writer.end(); } writer.end(); attributes() Adds additional name/value pairs to the current element. When a
[CONF] Apache Tapestry HTTPS
HTTPS Page added by Ulrich Stärk Securing your application with HTTPS Tapestry assumes your application will be primarily deployed as a standard web application, using HTTP (not HTTPS) as the transport mechanism. However, many applications will need to have some of their pages secured: only accessible via HTTPS. This could be a login page, or a product ordering wizard, or administrative pages. All that is necessary to mark a page as secure is to add the Secure|../apidocs/org/apache/tapestry5/annotations/Secure.html annotation to the page class: @Secure public class ProcessOrder { . . . } When a page is marked as secure, Tapestry will ensure that access to that page uses HTTPS. All links to the page will use the "https" protocol. If an attempt is made to access a secure page using a non-secure request (a normal HTTP request), Tapestry will send an HTTPS redirect to the client. Links to non-secure pages from a secure page will do the reverse: a complete URL with an "http" protocol will be used. In other words, Tapestry manages the transition from insecure to secure and back again. Links to other (secure) pages and to assets will be based on relative URLs and, therefore, secure. The rationale behind using secure links to assets from secure pages is that it prevents the client web browser from reporting a mixed security level. Securing Multiple Pages Rather than placing an @Secure annotation on individual pages, it is possible to enable security for folders of pages. All pages in or beneath the folder will be secured. This is accomplished by making a contribution to the MetaDataLocator|../apidocs/org/apache/tapestry5/services/MetaDataLocator.html service configuration. For example, to secure all pages in the "admin" folder: public void contributeMetaDataLocator(MappedConfigurationString,String configuration) { configuration.add("admin:" + MetaDataConstants.SECURE_PAGE, "true"); } Here "admin" is the folder name, and the colon is a separator between the folder name and the the meta data key. SECURE_PAGE is a public constant for value "tapestry.secure-page"; When Tapestry is determining if a page is secure or not, it starts by checking for the @Secure annotation, then it consults the MetaDataLocator service. If you want to make your entire application secure: public void contributeMetaDataLocator(MappedConfigurationString,String configuration) { configuration.add(MetaDataConstants.SECURE_PAGE, "true"); } With no colon, the meta data applies to the entire application (including any component libraries used in the application). Base URL Support When Tapestry switches back and forth between secure and unsecure mode, it must create a full URL (rather than a relative URL) that identifies the protocol, server host name and perhaps even a port number. That can be a stumbling point, especially the server host name. In a cluster, behind a fire wall, the server host name available to Tapestry, via the HttpServletRequest.getServerName() method, is often not the server name the client web browser sees ... instead it is the name of the internal server behind the firewall. The firewall server has the correct name from the web browser's point of view. Because of this, Tapestry includes a hook to allow you to override how these default URLs are created: the BaseURLSource|../apidocs/org/apache/tapestry5/services/BaseURLSource.html service. The default implementation is based on just the getServerName() method; it's often not the correct choice even for development. Fortunately, it is very easy to override this implementation. Here's an example of an override that uses the default port numbers that the Jetty servlet container uses for normal HTTP (port 8080) and for secure HTTPS (port 8443): public static void contributeAlias(ConfigurationAliasContribution configuration) { BaseURLSource source = new BaseURLSource() { public String getBaseURL(boolean secure) { String protocol = secure ? "https" : "http"; int port = secure ? 8443 : 8080; return String.format("%s://localhost:%d", protocol, port); } }; configuration.add(AliasContribution.create(BaseURLSource.class, source)); } This override is hardcoded to generate URLs for localhost; as such you might use it for development but certainly not in production. The Alias service exists just for these kinds of overrides; it allows a late-binding to a customized implementation of the BaseURLSource service that hides the built-in Tapestry implementation. Development Mode When working in development mode, the Secure annotation is ignored. This is controlled by the tapestry.secure-enabled configuration symbol. Application Server Configuration Setting up HTTPS support varies from application server to application server. Jetty: Jetty 6 Tomcat:
[CONF] Apache Tapestry Input Validation
Input Validation Page added by Ulrich Stärk Form Input and Validation The life's blood of any application is form input; this is the most effective way to gather significant information from the user. Whether it's a search form, a login screen or a multi-page registration wizard, forms are how the user really expresses themselves to the application. Tapestry excels at creating forms and validating input. Input validation is declarative, meaning you simply tell Tapestry what validations to apply to a given field, and it takes care of it on the server and (once implemented) on the client as well. Finally, Tapestry is able to not only present the errors back to the user, but to decorate the fields and the labels for the fields, marking them as containing errors (primarily, using CSS effects). Form component The core of Tapestry's form support is the Form|../tapestry-core/ref/org/apache/tapestry5/corelib/components/Form.html component. The Form component encloses (wraps around) all the other field components such as TextField|../tapestry-core/ref/org/apache/tapestry5/corelib/components/TextField.html, TextArea|../tapestry-core/ref/org/apache/tapestry5/corelib/components/TextArea.html, Checkbox|../tapestry-core/ref/org/apache/tapestry5/corelib/components/Checkbox.html, etc. The Form component generates a number of component events that you may provide event handler methods for. When rendering, the Form component emits two notifications: first, "prepareForRender", then "prepare". These allow the Form's container to setup any fields or properties that will be referenced in the form. For example, this is a good chance to create a temporary entity object to be rendered, or to load an entity from a database to be editted. When user submits the form on the client, a series of steps occur on the server. First, the Form emits a "prepareForSubmit" notification, then a "prepare" notification. These allow the container to ensure that objects are set up and ready to receive information from the form submission. Next, all the fields inside the form are activated to pull values out of the incoming request, validate them and (if valid) store the changes. _For Tapestry 4 Users: _ Tapestry 5 does not use the fragile "form rewind" approach from Tapestry 4. Instead, a hidden field generated during the render stores the information needed to process the form submission. After the fields have done their processing, the Form emits a "validateForm" event. This is a chance to perform cross-form validation that can't be described declaratively. Next, the Form determines if there have been any validation errors. If there have been, then the submission is considered a failure, and a "failure" event is emitted. If there have been no validation errors, then a "success" event is emitted. Last, the Form emits a "submit" event (for logic that doesn't care about success or failure). Tracking Validation Errors Associated with the Form is an ValidationTracker|../apidocs/org/apache/tapestry5/ValidationTracker.html that tracks all the provided user input and validation errors for every field in the form. The tracker can be provided to the Form via the Form's tracker parameter, but this is rarely necessary. The Form includes methods isValid() and getHasErrors(), which are used to see if the Form's validation tracker contains any errors. In your own logic, it is possible to record your own errors. Form includes two different versions of method recordError(), one of which specifies a Field|../apidocs/org/apache/tapestry5/Field.html (an interface implemented by all form element components), and one of which is for "global" errors, unassociated with any particular field. Storing Data Between Requests As with other action requests, the result of a form submission is to send a redirect to the client which re-renders the page. The ValidationTracker must be stored persistently between requests, or all the validation information will be lost (the default ValidationTracker provided by the Form is persistent). Likewise, the individual fields updated by the components should also be persistent. For example, a Login page, which collects a user name and a password, might look like: public class Login { @Persist private String userName; private String password; @Inject private UserAuthenticator authenticator; @Component(id = "password") private PasswordField passwordField; @Component private Form form; String onSuccess() { if (!authenticator.isValid(userName, password)) { form.recordError(passwordField, "Invalid user name or password."); return null; } return "PostLogin"; } public String getPassword() { return password; } public void setPassword(String password) { password = password; } public String getUserName() {
[CONF] Apache Tapestry Layout Component
Layout Component Page added by Ulrich Stärk Layout Component You may see frequent reference to a Layout Component, but you won't find it in the component reference|../tapestry-core/ref/index.html. Layout isn't a component, it's a component pattern. A Layout component exists to provide common content across all pages in your application. In traditional servlet development, you may be familiar with the use of a JSP include to include a banner across the top of your page and a copyright message across the bottom. Tapestry doesn't have a mechanism for such includes, nor does it have the need. Instead, you can create a component that acts like a template for your pages. Layout.tml html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd" head titleMy Nifty Web Application/title /head body div class="nav-top" Nifty Web Application /div t:body/ div class="nav-bottom" (C) 2008 NiftyWebCo, Inc. /div /body /html Layout is a standard component, with a standard component template. Like all component templates, it will be stored on the classpath (i.e., under src/main/resources). The magic is in the t:body/ element in the center; this will be replaced by the page's content, whatever that is. The two div elements above and below the t:body are, in this example, placeholders for the typical content you'll see in a web application: banners (and banner ads!), menus, login forms and so forth. Often these get very complex ... in fact, in most applications, the Layout component grows to be more complex than almost any page in the application. Remember that if you include a link to a resource such as an image or a stylesheet, you must use an absolute URL. The same component will be used for pages in many different folders, or with many different activation contexts, meaning that relative URLs are not only different for different pages, but may shift unexpectedly. Layout.java @IncludeStylesheet("context:css/site.css") public class Layout { } Components must always have a Java class. In this trivial example, the Layout component does not have much logic. We can save ourselves some typing using the @IncludeStylesheet|../apidocs/org/apache/tapestry5/annotations/IncludeStylesheet.html annotation (as opposed to directly adding the link element to the template. Index.tml html t:type="layout" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd" h1Welcome to the Nifty Web Application!/h1 p Would you like to t:pagelink page="login"Log In/t:pagelink? /p /html This is an example of using the Layout component. To keep our Index.tml template relatively previewable, we are using an html element and the t:type attribute to specify that it is a component. The html tag will be removed, and replaced with the content from the Layout.tml template (which convieniently starts with an html element). The t:body in Layout.tml will be replaced with the page specific content here: the h1 and p tags. Any page in the application that follows this pattern, using the Layout component, will have the same look and feel. You may find that your application has more than one look and feel: perhaps user registration pages have one look, while administrative pages have another. This can be accomplished by having multiple Layout components and using different layout types for different pages. Change Notification Preferences View Online
[CONF] Apache Tapestry Logging
Logging Page added by Ulrich Stärk Logging of Tapestry Components and Pages Tapestry makes extensive use of SLF4J to log details about the creation and operation of your page and component classes. The default configuration for logging uses Log4J as the logging toolkit, though this can be changed|../../tapestry-ioc/logging.html. Class to Logger The logger name for a page or component matches the fully qualified class name. You can configure this in log4j.properties: log4j.category.org.apache.tapestry5.integration.app1.pages.MerryChristmas=trace Injecting Loggers You may mark a field of type Logger with the @Inject annotation. The proper Logger for your page or component will be injected. public class MyPage { @Inject private Logger logger; . . . @Log annotation You may mark any component method with the Log|../apidocs/org/apache/tapestry5/annotations/Log.html annotation. Method entry, exit (and any thrown exceptions) will be logged at DEBUG level, along with parameter values and the method's return value. This is very convienient for debugging, especially when placed on event handler methods. Component Transformation Debugging Tapestry performs a transformation on your classes as they are loaded, often you want to gain insight into what it has done. Tapestry uses a secondary logger, consisting of the class name with the prefix "tapestry.transformer.", to log (at debug level) the results of transforming the class. Example: [DEBUG] MerryChristmas Finished class transformation: InternalClassTransformation[ public org.apache.tapestry5.integration.app1.pages.MerryChristmas extends java.lang.Object implements org.apache.tapestry5.runtime.Component, org.apache.tapestry5.runtime.RenderCommand add default method: public void postRenderCleanup() default add default method: public void setupRender(org.apache.tapestry5.MarkupWriter $1, org.apache.tapestry5.runtime.Event $2) default add default method: public void beginRender(org.apache.tapestry5.MarkupWriter $1, org.apache.tapestry5.runtime.Event $2) default add default method: public void beforeRenderTemplate(org.apache.tapestry5.MarkupWriter $1, org.apache.tapestry5.runtime.Event $2) default add default method: public void afterRenderTemplate(org.apache.tapestry5.MarkupWriter $1, org.apache.tapestry5.runtime.Event $2) default add default method: public void beforeRenderBody(org.apache.tapestry5.MarkupWriter $1, org.apache.tapestry5.runtime.Event $2) default add default method: public void afterRenderBody(org.apache.tapestry5.MarkupWriter $1, org.apache.tapestry5.runtime.Event $2) default add default method: public void afterRender(org.apache.tapestry5.MarkupWriter $1, org.apache.tapestry5.runtime.Event $2) default add default method: public void cleanupRender(org.apache.tapestry5.MarkupWriter $1, org.apache.tapestry5.runtime.Event $2) default add default method: public boolean handleComponentEvent(org.apache.tapestry5.runtime.ComponentEvent $1) default add default method: public org.apache.tapestry5.ComponentResources getComponentResources() default add default method: public void containingPageDidLoad() default add default method: public void containingPageDidDetach() default add default method: public void containingPageDidAttach() default add field: protected final org.apache.tapestry5.internal.InternalComponentResources _$resources; replace method: public final org.apache.tapestry5.ComponentResources getComponentResources() return _$resources; add default method: public void render(org.apache.tapestry5.MarkupWriter $1, org.apache.tapestry5.runtime.RenderQueue $2) default replace method: public void render(org.apache.tapestry5.MarkupWriter $1, org.apache.tapestry5.runtime.RenderQueue $2) _$resources.queueRender($2); convert default constructor: initializer(); add constructor: org.apache.tapestry5.integration.app1.pages.MerryChristmas(org.apache.tapestry5.internal.InternalComponentResources $1) { _$resources = $1; initializer(); } ] Is this helpful? Probably only if you are developing your own code that integrates into the component class transformation chain; for example, to support your own field and method annotations. Component Event Debugging Tapestry can also debug component event logic. The component's logger, with a "tapestry.events." prefix, is used at debug level. The debugging output identifies the event name and event source, and identifies any methods that are invoked. Note that events that are not handled by a component will bubble up to the component's container; further logging for the same event will occur using the logger associated with the container. The page containing the initial component is the final step when logging. Examples: [DEBUG] ActionLink Dispatch event: ComponentEvent[action from (self)] [DEBUG] ActionDemo Dispatch event: ComponentEvent[action from actionlink] [DEBUG] ActionDemo Invoking:
[CONF] Apache Tapestry Page Lifecycle
Page Lifecycle Page added by Ulrich Stärk Page Lifecycle In Tapestry, you are free to develop your presentation objects, page and components classes, as ordinary objects, complete with instance variables and so forth. This is somewhat revolutionary in terms of web development in Java. Using servlets, or Struts, your presentation objects (Servlets, or Struts Actions, or the equivalent in other frameworks) are stateless singletons. That is, a single instance is created, and all incoming requests are threaded through that single instance. Because multiple requests are handled by many different threads, this means that the single instance's variable are useless ... any value written into an instance variable would immediately be overwritten by a different thread. Thus, it is necessary to use the Servlet API's HttpServletRequest object to store per-request data, and the HttpSession object to store data between requests. Tapestry takes a very different approach. In Tapestry, you will have many different instances of any particular page, each either in use for a single request (on a single thread), or waiting in a page pool to be used. By reserving page instances to particular threads, all the difficult, ugly issues related to multi-threading go by the wayside. Instead, familiar, simple coding practices (using ordinary methods and fields) can be used. However, there's a risk: it would be a disaster if data could "bleed" from one request to another. Imagine the outcome in a banking application if the first user's account number and password became the default for the second user to reach the application! Tapestry takes special care to purge all instance variables back to their default value at the end of each request. The end result is that all pages in the pool are entirely equivalent to each other; it doesn't matter which instance is used for processing any particular request. Remember that the page instance is just the tip of the iceberg: a page instance encompasses the page component, its templates, all of its parameter bindings, tokens read from its template and (recursively) the same thing for all components inside the page. It adds up. A page instance will be "checked out" of the pool for a short period of time: a few milliseconds to service a typical request. Because of this, it is generally the case that Tapestry can handle a large number of end users with a relatively small pool of page instances. Comparison to JavaServer Pages JSPs also use a caching mechanism; the JSP itself is compiled into a Java servlet class, and acts as a singleton. However, the individual JSP tags are pooled. This is one of the areas where Tapestry can significantly outperform JSPs. Much of the code inside a compiled JSP class concerns getting tags from a tag pool, configuring the properties of the tag instance, using the tag instance, then cleaning up the tag instance and putting it back in the pool. The operations Tapestry does once per request are instead executed dozens or potentially hundreds of times (dependending the complexity of the page, and if any nested loops occur). Pooling JSP tags is simply the wrong granularity. Tapestry can also take advantage of its more coarse grained caching to optimize how data moves, via parameters, between components. This means that Tapestry pages will actually speed up after they render the first time. Page Pool Configuration Tapestry's page pool is used to store page instances. The pool is "keyed" on the name of the page (such as "start") and the locale for the page (such as "en" or "fr"). Within each key, Tapestry tracks the number of page instances that have been created, as well as the number that are in use (currently attached to a request). When a page is first accessed in a request, it is taken from the pool. Tapestry has some configuration values that control the details of how and when page instances are created. If a free page instance is available, the page is marked in use and attached to the request. If there are fewer page instances than the soft limit, then a new page instance is simply created and attached to the request. If the soft limit has been reached, Tapestry will wait for a short period of time for a page instance to become available before creating a new page instance. If the hard limit has been reached, Tapestry will throw an exception rather than create a new page instance. Otherwise, Tapestry will create a new page instance. Thus a busy application will initially create pages up-to the soft limit (which defaults to five page instances). If the application continues to be pounded with requests, it will slow its request processing, using the soft wait time in an attempt to reuse an existing page instance. A truly busy application will continue to create new page instances as needed until the hard limit is reached. Remember that all these configuration values are per key:
[CONF] Apache Tapestry Page Navigation
Page Navigation Page added by Ulrich Stärk Page Navigation In essense, a Tapestry application is a number of related pages, working together. To some degree, each page is like an application unto itself. Any individual request will be targetted at a single page. Requests come in two forms: component event requests target a specific component on a specific page, triggering an event within that component render requests target a specific page, and stream the HTML markup for that page back to the client This dictomy between component event requests and render requests is new in Tapestry 5. It is in some ways based on ideas from the Portlet specification and differentiating the two types of requests alleviates a number of problems in traditional web applications related to the browser back button, or to the user hitting the refresh button in their browser. Component Event Requests Component event requests may take the form of hyperlinks (ActionLink|../ref/org/apache/tapestry5/corelib/components/ActionLink.html) or form submissions (Form|../ref/org/apache/tapestry5/corelib/components/Form.html). In both cases, the value returned from an event handler method controls the response sent to the client web browser. The URL for a component event request identifies the name of the page, the nested id of the component, and the name of the event to trigger on the component (this is usually "action"). Further, a component event request may contain additional context information, which will be provided to the event handler method. These URLs expose a bit of the internal structure of the application. Over time, as an application grows and is maintained, the ids of components may change. This means that component event request URLs should not be bookmarked. Fortunately, users will rarely have the chance to do so (see below). Null response If the event handler method returns no value, or returns null, then the current page (the page containing the component) will render the response. A page render link for the current page is created and sent to the client as a client side redirect. The client browser will automatically submit a new request to generate the page. The user will see the newly generated content in their browser. In addition, the URL in the browser's address bar will be a render request URL. Render request URLs are shorter and contain less application structure (for instance, they don't include component ids or event types). Render requests URLs are what your users will bookmark. The component event request URLs are transitory, meaningful only while the application is actively engaged, and not meant to be used in later sessions. String response When a string is returned, it is expected to be the logical name of a page (as opposed to the page's fully qualified class name). As elsewhere, the name of the page is case insensitive. Again, a render request URL will be constructed and sent to the client as a redirect. Class response When a class is returned, it is expected to be a page class. Returning a page class from an event handler is safer for refactoring than returning a page name. As with other response types, a render request URL will be constructed and sent to the client as a redirect. Page response You may also return an instance of a page, rather than the name or class of a page. A page may be injected via the InjectPage|../apidocs/org/apache/tapestry5/annotations/InjectPage.html annotation. Often, you will configure the page in some way before returning the page (examples below). You can also return a component within the page, but this will generate a runtime warning. Link response An event handler method may return a Link|../apidocs/org/apache/tapestry5/Link.html instance directly. The Link is converted into a URL and a client redirect to that URL is sent to the client. The ComponentResources|../apidocs/org/apache/tapestry5/ComponentResources.html object that is injected into your pages (and components) has methods for creating component event and page render links. Stream response An event handler can also return a StreamResponse|../apidocs/org/apache/tapestry5/StreamResponse.html object, which encapsulates a stream to be sent directly to the client browser. This is useful for compnents that want to, say, generate an image or PDF and provide it to the client. URL response A URL is handled as a client redirect to an external URL. Object response Any other type of object returned from an event handler method is an error. Page Render Requests Render requests are simpler in structure and behavior than component event requests. In the simplest case, the URL is simply the logical name of the page. Pages may have an activation context. The activation context represents persistent information about the state of the page. In practical terms, the activation context is usually the id of some database-persistent object.
[CONF] Apache Tapestry Persistent Page Data
Persistent Page Data Page added by Ulrich Stärk Persistent Page Data Most instance variables in Tapestry are automatically cleared at the end of each request. This is important, as it pertains to how Tapestry pages are pooled and shared, over time, by many users. However, you often want to store some persistent data on a single page, and have access to it in later requests. Long term storage of data should go in a database of some form, but server-side state for the duration of the as user's interaction with the application should go in the HttpSession (though Tapestry provides a few other options as well). Note: To store values that may be accessed across multiple pages, uses a session state object. Making a field persistent is accomplished with the Persist annotation|../apidocs/org/apache/tapestry5/annotations/Persist.html. Again, this does not refer to database persistence, it refers to session persistance. This annotation is applied to private instance fields of components. @Persist private int value; Annotated fields will store their state between requests. Generally, speaking, this means that the value is stored into the session (but other approaches are possible). Whenever you make a change to a persistent field, its value is stored. On later requests, the value for such persistent fields is reloaded from storage. Persistence Strategies The value for each field is the strategy used to store the field between requests. session strategy The session strategy stores field changes into the session; the session is created as necessary. A suitably long session attribute name is used; it incorporates the name of the page, the nested component id, and the name of the field. Session strategy is the default strategy used unless otherwise overridden. flash strategy The flash strategy stores information in the session as well, just for not very long. Values are stored into the session, but then deleted from the session as they are first used to restore a page's state. The flash is typically used to store temporary messages that should only be displayed to the user once. client strategy The field is persisted onto the client; you will see an additional query parameter in each URL (or an extra hidden field in each form). Client persistence is somewhat expensive. It can bloat the size of the rendered pages by adding hundreds of characters to each link. There is extra processing on each request to de-serialize the values encoded into the query parameter. Client persistence does not scale very well; as more information is stored into the query parameter, its length can become problematic. In many cases, web browsers, firewalls or other servers may silently truncate the URL which will break the application. Use client persistence with care, and store a minimal amount of data. Try to store the identity (that is, primary key) of an object, rather than the object itself. Persistence Search By default the value for the Persist annotation is the empty string. When this is true, then the actual strategy to be used is determined by a search up the component hiearchy. For each component, the meta-data property tapestry.persistence-strategy is checked. This can be specified using the Meta|../apidocs/org/apache/tapestry5/annotations/Meta.html annotation. If the value is non-blank, then that strategy is used. This allows a component to control the persistence strategy used inside any sub-components (that don't explicitly use a different strategy). In any case, if no component provides the meta data, then the ultimate default, "session", is used. Default Values Fields marked with @Persist may not have default values (whether set inline, or inside a constructor). Clearing Persistent Fields If you reach a point where you know that all data for a page can be discarded, you can do exactly that. The method discardPersistentFieldChanges() of ComponentResources will discard all persistent fields for the page, regardless of which strategy is used to store the property. This will not affect the page in memory, but takes effect for subsequent requests. Clustering Issues The Servlet API was designed with the intention that there would be only a modest amount of server-side state, and that the stored values would be individual numbers and strings, and thus, immutable. Many web frameworks do not use the HttpSession this way, and store large and mutable objects in the session. This is not an issue for single servers, but in a cluster, anything stored in the session must be serialized to a bytestream and distributed to other servers within the cluster, and restored there. Most application servers perform the serialization and distribution as part of HttpSession.setAttribute(). This creates a problem for mutable objects, because if you read a mutable session object, change its state, but don't invoke setAttribute(), the changes will be
[CONF] Apache Tapestry Persistent State
Persistent State Page added by Ulrich Stärk Persistent State Often, you will have a situation where you have a bit of data that is needed across multiple pages. Perhaps you are creating a multi-page wizard, or perhaps you have an object that tracks the user's identify once logged in. Ordinary persistent page data is not appropriate, since persistent fields apply to a specific page and aren't shared across pages. Instead, you want to use a Session State Object (an SSO). With an SSO, the value is automatically stored outside the page; with the default storage strategy, it is stored in the session. Such a value is global to all pages for the same user, but is stored seperately for different users. A field holding an SSO is marked with the SessionState|../apidocs/org/apache/tapestry5/annotations/SessionState.html annotation. Example: public class MyPage { @SessionState private MyState myState; . . . } Any other component or page that declares a field of the same type, regardless of name, and marks it with the SessionState annotation will share the same value. It's that simple. For Tapestry 4 Users: a big change here is that you don't need to provide any configuration for the SSO before using it, nor do you provide a logical name. Tapestry 5 uses the class name to identify the SSO, so there's no need for a logical name. The first time you access an SSO, it is created automatically. Typically, the SSO will have a public no-args constructor ... but you may inject dependencies into the SSO via its constructor, as you can with a Tapestry IoC service implementation. Assigning a value to an SSO field will store that value. Assigning null to an SSO field will remove the SSO (reading the field subsequently will force a new SSO instance to be created). Check for Creation Scalable web applications do not create the server-side session needlessly. If you can avoid creating the session, especially on first access to your web application, you will be able to handle an order of magnitude more users. So, if you can avoid creating the SSO, you should do so. But how to avoid creating it? Simply checking ("myState != null") will force the creation of the SSO and the session to store it in. Instead, create a second field: private boolean myStateExists; This companion field is used to see if the SSO already exists. It is not annotated; it is located by name ("Exists" is appended to the name of the field storing the SSO). It must be type boolean and must be a private instance variable. Alternately, you may allow for the state being null: @SessionState(create=false) private MyState myState; In this case, the myState field will be null if the MyState SSO does not exist, but will be non-null if it has been created (either by assigning a value to the field, or by a different SSO field where create is true). Persistence Strategies Each SSO is managed according to a persistence strategy. The default persistence strategy, "session", stores the SSOs inside the session. The session is created as needed. Clustering Issues The clustering strategy for Application State Objects in release 5.0 now applies to all session-persisted objects. See the persistent page data notes for more details. Configuring SSOs Generally, you will configure an SSO if you need to change it from the default persistence strategy. Right now there's only one built in strategy, but more will be coming in the future. Alternately, you will configure an SSO so that you can control how it is instantiated. You may need to inject some values into the SSO when it is first created, or otherwise initialize it. In this second case, you may provide an ApplicationStateCreator|../apidocs/org/apache/tapestry5/services/ApplicationStateCreator.html object, which will be called upon to create the SSO as necessary. This is also the technique to use when you want your SSO to be represented by an interface rather than a class: you need to provide a creator that knows about the class that implements the interface. Contributions to the ApplicationStateManager service are used to configure an SSO. From your application's module: public void contributeApplicationStateManager(MappedConfigurationClass, ApplicationStateContribution configuration) { ApplicationStateCreatorMyState creator = new ApplicationStateCreatorMyState() { public MyState create() { return new MyState(new Date()); } }; configuration.add(MyState.class, new ApplicationStateContribution("session", creator)); } Here, we have an SSO type of MyState, and we're providing a creator for it. We've dolled the creator up with some generic types, but that isn't essential. Our creator creates a new MyState instance using an alternate constructor that takes the current date and time. Again, just an example. Finally, we create an
[CONF] Apache Tapestry Project Layout
Project Layout Page added by Ulrich Stärk Project Layout This is the suggested layout for your Tapestry project; it is the layout of folders and files created by the Tapestry Quickstart Archetype|../../quickstart/. If you are creating your own build using Ant, you may use whatever conventions work for you ... as long as everything gets packaged up into the right place in the target WAR. Parts of this project layout mimics the format of an exploded WAR (a WAR file unpackaged onto the file system). This will often enable you to run your application directly from your workspace, without any special build or packaging process, while developing. Each of the major IDEs has plugins to allow you to accomplish this task ... and its one of the factors (combined with live class reloading) that makes working with Tapestry a breeze. Below is a sample project, whose root package is com.example.myapp: !../images/projectlayout.png!Project Layouth2. Main source files Main Java source files, the files that will be compiled into the WAR file, are in src/main/java. This is only Java source files. You can see the Index.java source file inside the pages subpackage, and the Layout.java source file inside the components subpackage. The package names demonstrated here are required, dictated by the rules for component classes. Compiled Java classes will ultimately be packaged in the WAR inside the WEB-INF/classes folder. Classpath Resources Resource files are under src/main/resources. This includes the message catalog for the Index page (Index.properties), as well as the message catalog and component template for the Layout component (Layout.tml). These files will also be packaged into the WEB-INF/classes folder of the WAR. Component templates will always be stored in the resources folder. Templates for pages may be packaged in the WAR proper instead. Context Resources The WAR is built primarily from the src/main/webapp folder; this is where ordinary files are stored (such as images and stylesheets). Page templates may also be stored here (Index.tml). The file src/main/webapp/WEB-INF/web.xml is the servlet container deployment descriptor, which has a very specific configuration for Tapestry. The build tool (usually Maven) will be responsible for putting compiled classes and resources into the WEB-INF/classes folder of the WAR, and for putting the Tapestry library, and its dependencies (as well as any additional libraries defined by your application) into the WEB-INF/lib folder. Testing The folders src/test/java and src/test/resources are used when compiling and executing tests. Files in these folders are not packaged into the final WAR. Change Notification Preferences View Online
[CONF] Apache Tapestry Project Layout
Project Layout Page edited by Ulrich Stärk Changes (3) ... !../images/projectlayout.png!Project Layouth2. Main source files !../images/projectlayout.png!Project Layout h2. Main source files Main Java source files, the files that will be compiled into the WAR file, are in {{src/main/java}}. This is _only_ Java source files. You can see the {{Index.java}} source file inside the {{pages}} subpackage, and the {{Layout.java}} source file inside the {{components}} subpackage. The package names demonstrated here are required, dictated by the rules for [component classes|#component-classes.html]. ... Full Content Project Layout This is the suggested layout for your Tapestry project; it is the layout of folders and files created by the Tapestry Quickstart Archetype|../../quickstart/. If you are creating your own build using Ant, you may use whatever conventions work for you ... as long as everything gets packaged up into the right place in the target WAR. Parts of this project layout mimics the format of an exploded WAR (a WAR file unpackaged onto the file system). This will often enable you to run your application directly from your workspace, without any special build or packaging process, while developing. Each of the major IDEs has plugins to allow you to accomplish this task ... and its one of the factors (combined with live class reloading) that makes working with Tapestry a breeze. Below is a sample project, whose root package is com.example.myapp: !../images/projectlayout.png!Project Layout Main source files Main Java source files, the files that will be compiled into the WAR file, are in src/main/java. This is only Java source files. You can see the Index.java source file inside the pages subpackage, and the Layout.java source file inside the components subpackage. The package names demonstrated here are required, dictated by the rules for component classes. Compiled Java classes will ultimately be packaged in the WAR inside the WEB-INF/classes folder. Classpath Resources Resource files are under src/main/resources. This includes the message catalog for the Index page (Index.properties), as well as the message catalog and component template for the Layout component (Layout.tml). These files will also be packaged into the WEB-INF/classes folder of the WAR. Component templates will always be stored in the resources folder. Templates for pages may be packaged in the WAR proper instead. Context Resources The WAR is built primarily from the src/main/webapp folder; this is where ordinary files are stored (such as images and stylesheets). Page templates may also be stored here (Index.tml). The file src/main/webapp/WEB-INF/web.xml is the servlet container deployment descriptor, which has a very specific configuration for Tapestry. The build tool (usually Maven) will be responsible for putting compiled classes and resources into the WEB-INF/classes folder of the WAR, and for putting the Tapestry library, and its dependencies (as well as any additional libraries defined by your application) into the WEB-INF/lib folder. Testing The folders src/test/java and src/test/resources are used when compiling and executing tests. Files in these folders are not packaged into the final WAR. Change Notification Preferences View Online | View Changes
[CONF] Apache Tapestry Project Layout
Project Layout File attached by Ulrich Stärk projectlayout.png (50 kB image/png) Change Notification Preferences View Attachments
[CONF] Apache Tapestry Project Layout
Project Layout Page edited by Ulrich Stärk Changes (2) ... !../images/projectlayout.png!Project Layout !projectlayout.png|title=Project Layout! h2. Main source files ... Full Content Project Layout This is the suggested layout for your Tapestry project; it is the layout of folders and files created by the Tapestry Quickstart Archetype|../../quickstart/. If you are creating your own build using Ant, you may use whatever conventions work for you ... as long as everything gets packaged up into the right place in the target WAR. Parts of this project layout mimics the format of an exploded WAR (a WAR file unpackaged onto the file system). This will often enable you to run your application directly from your workspace, without any special build or packaging process, while developing. Each of the major IDEs has plugins to allow you to accomplish this task ... and its one of the factors (combined with live class reloading) that makes working with Tapestry a breeze. Below is a sample project, whose root package is com.example.myapp: Main source files Main Java source files, the files that will be compiled into the WAR file, are in src/main/java. This is only Java source files. You can see the Index.java source file inside the pages subpackage, and the Layout.java source file inside the components subpackage. The package names demonstrated here are required, dictated by the rules for component classes. Compiled Java classes will ultimately be packaged in the WAR inside the WEB-INF/classes folder. Classpath Resources Resource files are under src/main/resources. This includes the message catalog for the Index page (Index.properties), as well as the message catalog and component template for the Layout component (Layout.tml). These files will also be packaged into the WEB-INF/classes folder of the WAR. Component templates will always be stored in the resources folder. Templates for pages may be packaged in the WAR proper instead. Context Resources The WAR is built primarily from the src/main/webapp folder; this is where ordinary files are stored (such as images and stylesheets). Page templates may also be stored here (Index.tml). The file src/main/webapp/WEB-INF/web.xml is the servlet container deployment descriptor, which has a very specific configuration for Tapestry. The build tool (usually Maven) will be responsible for putting compiled classes and resources into the WEB-INF/classes folder of the WAR, and for putting the Tapestry library, and its dependencies (as well as any additional libraries defined by your application) into the WEB-INF/lib folder. Testing The folders src/test/java and src/test/resources are used when compiling and executing tests. Files in these folders are not packaged into the final WAR. Change Notification Preferences View Online | View Changes
[CONF] Apache Tapestry Request Processing
Request Processing Page added by Ulrich Stärk Request Processing Understanding the request processing pipeline is very important, as it is one of the chief extension points for Tapestry. Much of the early stages of processing are in the form of extensible pipelines|../tapestry-ioc/pipeline.html. Tapestry Filter All incoming requests originate with the TapestryFilter, which is configured inside the application's web.xml. The TapestryFilter is responsible for a number of startup and initialization functions. When it receives a request, the TapestryFilter obtains the HttpServletRequestHandler|../apidocs/org/apache/tapestry5/services/HttpServletRequestHandler.html service, and invokes its service() method. HttpServletRequestHandler Pipeline This pipeline performs initial processing of the request. It can be extended by contributing a HttpServletRequestFilter|../apidocs/org/apache/tapestry5/services/HttpServletRequestFilter.html into the HttpServletRequestHandler service's configuration.' Tapestry does not contribute any filters into this pipeline of its own The terminator for the pipeline does two things: It stores the request and response into the RequestGlobals|../apidocs/org/apache/tapestry5/services/RequestGlobals.html service. This is a threaded service that stores per-thread/per-request information. It wraps the request and response as a Request|../apidocs/org/apache/tapestry5/services/Request.html and Response|../apidocs/org/apache/tapestry5/services/Response.html, and passes them into the RequestHandler|../apidocs/org/apache/tapestry5/services/RequestHandler.html pipeline. Primarily, this exists to bridge from the Servlet API objects to the corresponding Tapestry objects. This is the basis for the planned portlet integration for Tapestry. RequestHandler Pipeline This pipeline is where most extensions related to requests take place. Request represents an abstraction on top of HttpServletRequest; this is necessary to support non-servlet applications, such as portlet applications. Where other code and services within Tapestry require access to information in the request, such as query parameters, that information is obtained from the Request (or Response) objects. The pipeline includes a number of built-in filters: CheckForUpdates is responsible for class and template reloading. Localization identifies the locale for the user. StaticFiles checks for URLs that are for static files (files that exist inside the web context) and aborts the request, so that the servlet container can handle the reuest normally. ErrorFilter catches uncaught exceptions from the lower levels of Tapestry and presents the exception report page. This involves the RequestExceptionHandler|../apidocs/org/apache/tapestry5/services/RequestExceptionHandler.html service, which is responsible for initializing and rendering the core/ExceptionReport|../apidocs/org/apache/tapestry5/corelib/pages/ExceptionReport.html page. The terminator for this pipeline stores the Request and the Response into RequestGlobals, then requests that the MasterDispatcher|../apidocs/org/apache/tapestry5/services/Dispatcher.html service figure out how to handle the request (if it is, indeed, a Tapestry request). Master Dispatcher Service The MasterDispatcher service is a chain-of-command, aggregating together (in a specific order), several Dispatcher objects. Each Dispatcher is built to recognize and process a particular kind of URL. RootPath As discussed elsewhere, requests for the context root will instead be treated like render requests for the "start" page. Asset Requests that being with "/assets/" are references to asset resources that are stored on the classpath, inside the Tapestry JARs (or perhaps inside the JAR for a component library). The contents of the file will be pumped down to the client browser. PageRender Page render requests are requests to render a particular page. Such requests may include additional elements on the path, which will be treated as activation context (generally speaking, the primary key of some related entity object), allowing the page to reconstruct the state it will need to succesfully render itself. The event handler method for the activate event may return a value; this is treated the same as the return value from a component action request; typically this will result in a redirect to another page. In this way, the activate event can perform simple validation at the page level ("can the user see this page?"). Page render URLs consist of the logical name of the page plus additional path elements for the activation context. The dispatcher here strips terms off of the path until it finds a known page name. Thus, "/mypage/27" would look first for a page whose name was "mypage/27", then look for a page name "mypage". Assuming the second search was succesful, the page would be activated with the context "27". If no logical page name can be
[CONF] Apache Tapestry Response Compression
Response Compression Page added by Ulrich Stärk Response Compression Starting in Tapestry 5.1, the framework automatically GZIP compresses content streamed to the client. This can signifcantly reduce the amount of network traffic for a Tapestry application, at the cost of extra processing time on the server to compress the response stream. This directly applies to both rendered pages and streamed assets from the classpath. Context assets will also be compressed ... but this requires referencing such assets using the "context:" binding prefix, so that generated URL is handled by Tapestry and not the servlet container. Compression Configuration Small streams generally do not benefit from being compressed; there is overhead when using compression, not just the CPU time to compress the bytes, but a lot of overhead. For small responses, Tapestry does not attempt to compress the output stream. The configuration symbol tapestry.min-gzip-size allows the cutoff to be set; it defaults to 100 bytes. In addition, some file types are already compressed and should not be recompressed (they actually get larger, not smaller!). The service ResponseCompressionAnalyzer|../apidocs/org/apache/tapestry5/services/ResponseCompressionAnalyzer.html's configuration is an unordered collection of content type strings that should not be compressed. The default content types are "image/jpeg". The mapping from file type (by extension) to content type is typically done as part of your servlet-containers configuration. Alternately, you can contribute to the ResourceStreamer service's configuration. This is a mapped configuration; it maps file extensions (such as "css" or "js") to content types ("text/css" or "text/_javascript_") respectively. StreamResponse When returning a StreamResponse|../apidocs/org/apache/tapestry5/StreamResponse.html from a component event method, the stream is totally under your control; it will not be compressed. You should use the ResponseCompressionAnalyzer service to determine if the client supports compression, and add a java.util.zip.GZIPOutputStream to your stream stack if compression is desired. Change Notification Preferences View Online
[CONF] Apache Tapestry Service Status
Service Status Page added by Ulrich Stärk IoC Services Status Using Tapestry there will often be a large number of services defined in the registry; a mix of the builtin services provided by the framework and your own. Built in to every Tapestry application is a page, ServiceStatus, that can present this information to you. The page "ServiceStatus" presents the list of services within the appication's Registry. Unable to render embedded object: File (servicestatus.png) not found. PageServices may be builtin, defined, virtual or real. Builtin only applies to a few special services that are part of Tapestry IoC. Defined services are defined in some module, but have not yet been referenced in any way. Virtual services have been referenced and have gotten as far as creating a service proxy. Real services have had methods invoked, this forces the realization of the service which includes instantiating the service, injecting dependencies, and decorating with any applicable interceptors. The Service Status page does not display service information when the application is in production mode. Change Notification Preferences View Online
[CONF] Apache Tapestry Service Status
Service Status File attached by Ulrich Stärk servicestatus.png (142 kB image/png) Change Notification Preferences View Attachments
[CONF] Apache Tapestry Type Coercion
Type Coercion Page added by Ulrich Stärk Parameter Type Coercions Tapestry automatically handles type coercions for parameters. Type coercions occur when a value passed into a parameter (as bound in template or in an annoation) does not match the type of the parameter. For example, consider the Count component: public class Count { @Parameter private int start = 1; @Parameter(required = true) private int end; @Parameter private int value; . . . Here, the type of all three parameters is int. However, it is likely that the component will be used as so: Merry Christmas: t:count end="3" Ho! /t:count A bare whole number is interpreted by the prop binding prefix as a long. So this is the long value 3. Tapestry will automatically coerce the bound value, a long, to the parameter's type, int. This may be a lossy coercion (if the long represents a number larger than can be stored in an int). TypeCoercer Service The TypeCoercer service is responsible for this type coercion. This service is part of the tapestry-ioc module, and is documented there|../tapestry-ioc/coerce.html. The service is quite extensible, allowing for new types and coercions to be added easily. The TapestryModule contributes a few additional coercions into the TypeCoercer service. Change Notification Preferences View Online
[CONF] Apache Tapestry Unit testing pages or components
Unit testing pages or components Page added by Ulrich Stärk Unit testing pages or components You can easily unit test a certain page or a component. Follow the simple tasks below. Setting up a driving environment In order to unit test a page, you'll need to create an instance of PageTester|../apidocs/org/apache/tapestry5/test/pagelevel/PageTester.html. It acts as both the browser and the servlet container so that you can use it to drive your page. As it is not a real servlet container, you need to tell it the same information as you would in web.xml: 1. Your application package. 1. Your filter name. This is used to load your Tapestry IoC module only. If you have none, you can pass an empty string or anything to it. 1. The folder acting as your context root. This is used to locate your templates (if they're put there).Here is an example (using TestNG, but you're free to use JUnit or anything else): public class MyTest extends Assert { @Test public void test1() { String appPackage = "org.example.app"; String appName = "App1"; // App1Module.java has configured some services. PageTester tester = new PageTester(appPackage, appName, "src/main/webapp"); } } Testing the rendering of a page To test if a page renders properly (optionally with context), you can tell the PageTester to render it and then assert against the DOM Document|../apidocs/org/apache/tapestry5/dom/Document.html returned. Here is an example. Let's assuming the page being tested is named "MyPage" and it should return a page containing an HTML element whose id is "id1" and whose text content should be "hello": public class MyTest extends Assert { @Test public void test1() { String appPackage = "org.example.app"; String appName = "LocaleApp"; PageTester tester = new PageTester(appPackage, appName, "src/main/webapp"); Document doc = tester.renderPage("MyPage"); assertEquals(doc.getElementById("id1").getChildText(), "hello"); } } If the page requires a context, you can pass it this way: public class MyTest extends Assert { @Test public void test1() { String appPackage = "org.example.app"; String appName = "LocaleApp"; PageTester tester = new PageTester(appPackage, appName, "src/main/webapp"); Object[] context = new Object[]{ "abc", 123 }; Document doc = tester.invoke(new ComponentInvocation(new PageLinkTarget("MyPage"), context)); assertEquals(doc.getElementById("id1").getChildText(), "hello"); } } Testing an action link After rendering a page, you may want to "click" on an action link and then assert against the resulting page. You can do it this way: public class MyTest extends Assert { @Test public void test1() { String appPackage = "org.example.app"; String appName = "LocaleApp"; PageTester tester = new PageTester(appPackage, appName, "src/main/webapp"); Document doc = tester.renderPage("MyPage"); Element link = doc.getElementById("link1"); doc = tester.clickLink(link); assertTrue(doc.toString().contains("abc")); } } Testing a form submission After rendering a page, you may want to fill out a form, submit it and then inspect the resulting page. You can do it this way: public class MyTest extends Assert { @Test public void test1() { String appPackage = "org.example.app"; String appName = "LocaleApp"; PageTester tester = new PageTester(appPackage, appName, "src/main/webapp"); Document doc = tester.renderPage("MyPage"); Element form = doc.getElementById("form1"); MapString, String fieldValues = new HashMapString, String(); fieldValues.put("field1", "hello"); fieldValues.put("field2", "100"); doc = tester.submitForm(form, fieldValues); assertTrue(doc.toString().contains("abc")); } } To submit a form by clicking a submit button, call the clickSubmit()|../apidocs/org/apache/tapestry5/test/pagelevel/PageTester.html#clickSubmitorg.apache.tapestry5.dom.Element_java.util.Map method instead. Unit testing a component To unit test a component, just create a test page containing that component. Then unit test that page. Change Notification Preferences View Online
[CONF] Apache Tapestry URL rewriting
URL rewriting Page added by Ulrich Stärk Tapestry URL Rewriting Support Since 5.1.0.1, Tapestry has basic support for URL rewriting. Incoming requests and links generated by Tapestry can be rewritten using exactly the same API. It is based on a chain of URLRewriterRule interfaces. These rules are executed before all other Tapestry request handling, so it does not even know that the received request is not the original one. Each URL rewriter rule, in its Request process, can choose between returning another Request, effectively rewriting it, or returning the received request unchanged, meaning that this rule does not apply to that request. To create facilitate Request creation, Tapestry provides the SimpleRequestWrapper class. It wraps an Request, delegating all methods except getPath() and getServerName(). More request wrappers may be added in the future on demand. Configuration Tapestry's URL rewriting support is configured by Tapestry-Ioc through contribution of URLRewriterRule}}s to the {{URLRewriter service. The following example is part of the Tapestry's tests. Simple example of rule chaining This example just rewrites all incoming requests to /struts to /tapestry. In your AppModule or any other Tapestry-IoC module class: public static void contributeURLRewriter(OrderedConfigurationURLRewriterRule configuration) { URLRewriterRule rule = new URLRewriterRule() { public Request process(Request request, URLRewriteContext context) { final String path = request.getPath(); if (path.equals("/struts")) { request = new SimpleRequestWrapper(request, "/tapestry"); } return request; } public RewriteRuleApplicability applicability() { return RewriteRuleApplicability.INBOUND; } }; configuration.add("myrule", rule); } Example of rule chaining In your AppModule or any other Tapestry-IoC module class. public static void contributeURLRewriter(OrderedConfigurationURLRewriterRule configuration) { URLRewriterRule rule1 = new URLRewriterRule() { public Request process(Request request, URLRewriteContext context) { final String path = request.getPath(); if (path.equals("/struts")) { request = new SimpleRequestWrapper(request, "/jsf"); } return request; } public RewriteRuleApplicability applicability() { return RewriteRuleApplicability.INBOUND; } }; URLRewriterRule rule2 = new URLRewriterRule() { public Request process(Request request, URLRewriteContext context) { final String path = request.getPath(); if (path.equals("/jsf")) { request = new SimpleRequestWrapper(request, "/tapestry"); } return request; } public RewriteRuleApplicability applicability() { return RewriteRuleApplicability.INBOUND; } }; URLRewriterRule rule3 = new URLRewriterRule() { public Request process(Request request, URLRewriteContext context) { String path = request.getPath(); if (path.equals("/tapestry")) { path = "/urlrewritesuccess"; request = new SimpleRequestWrapper(request, path); } return request; } public RewriteRuleApplicability applicability() { return RewriteRuleApplicability.INBOUND; } }; URLRewriterRule rule4 = new URLRewriterRule() { public Request process(Request request, URLRewriteContext context) { String serverName = request.getServerName(); String path = request.getPath(); final String pathToRewrite = "/urlrewritesuccess/login"; if (serverName.equals("localhost") path.equalsIgnoreCase(pathToRewrite)) { request = new SimpleRequestWrapper(request, "http://login.domain.com", "/"); } return request; } public RewriteRuleApplicability applicability() { return RewriteRuleApplicability.OUTBOUND; } }; configuration.add("rule1", rule1); configuration.add("rule2", rule2, "after:rule1"); configuration.add("rule3", rule3, "after:rule2"); configuration.add("rule4", rule4); } This examples shows the URL rewriting chaining: the first rule rewrites requests to /struts and rewrites them to /jsf and leaves requests to other URLs unchanged. The second rewrites /jsf to /tapestry and the third rewrites
[CONF] Apache Tapestry Input Validation
Input Validation File attached by Ulrich Stärk validation_errors.png (33 kB image/png) Change Notification Preferences View Attachments
[CONF] Apache Tapestry Input Validation
Input Validation File attached by Ulrich Stärk validation_initial.png (25 kB image/png) Change Notification Preferences View Attachments
[CONF] Apache Tapestry Input Validation
Input Validation File attached by Ulrich Stärk validation_minlength.png (32 kB image/png) Change Notification Preferences View Attachments
[CONF] Apache Tapestry Input Validation
Input Validation File attached by Ulrich Stärk validation_password.png (31 kB image/png) Change Notification Preferences View Attachments
[CONF] Apache Tapestry Input Validation
Input Validation Page edited by Ulrich Stärk Changes (12) ... Next, all the fields inside the form are activated to pull values out of the incoming request, validate them and (if valid) store the changes. _For Tapestry 4 Users: _ Tapestry 5 does not use the fragile form rewind approach from Tapestry 4. Instead, a hidden field generated during the render stores the information needed to process the form submission. After the fields have done their processing, the Form emits a validateForm event. This is a chance to perform cross-form validation that cant be described declaratively. ... !../images/validation_initial.png!Initial form presentationNotice how the Label components are displaying the textual names for the fields. Given that we have not done any explicit configuration, whats happened is that the components ids (userName and password) have been converted to User Name and Password. !validation_initial.png|title=Initial form presentation! Notice how the Label components are displaying the textual names for the fields. Given that we have not done any explicit configuration, whats happened is that the components ids (userName and password) have been converted to User Name and Password. If you just submit the form as is, the fields will violate the required constraint and the page will be redisplayed to present those errors to the user: !validation_errors.png|title=Errors and decorations! !../images/validation_errors.png!Errors and decorationsTheres a couple of subtle things going on here. First, Tapestry tracks _all_ the errors for _all_ the fields. The Errors component has displayed them at the top of the form. Further, the _default validation decorator_ has added decorations to the labels and the fields, adding t-error to the CSS class for the fields and labels. Tapestry provides a default CSS stylesheet that combines with the t-error class to make things turn red. Next, well fill in the user name but not provide enough characters for password. !../images/validation_minlength.png!Minlength error messageThe user name field is OK, but theres an error on just the password field. The PasswordField component always displays a blank value by default, otherwise wed see the partial password displayed inside. !validation_minlength.png|title=Minlength error message! The user name field is OK, but theres an error on just the password field. The PasswordField component always displays a blank value by default, otherwise wed see the partial password displayed inside. If you type in enough characters and submit, we see how the logic inside the Login page can attach errors to fields: !../images/validation_password.png!Application supplied errorsThis is nice and seamless; the same look and feel and behavior for both the built-in validators, and for errors generated based on application logic. !validation_password.png|title=Application supplied errors! This is nice and seamless; the same look and feel and behavior for both the built-in validators, and for errors generated based on application logic. h1. Centralizing Validation with @Validate ... Full Content Form Input and Validation The life's blood of any application is form input; this is the most effective way to gather significant information from the user. Whether it's a search form, a login screen or a multi-page registration wizard, forms are how the user really expresses themselves to the application. Tapestry excels at creating forms and validating input. Input validation is declarative, meaning you simply tell Tapestry what validations to apply to a given field, and it takes care of it on the server and (once implemented) on the client as well. Finally, Tapestry is able to not only present the errors back to the user, but to decorate the fields and the labels for the fields, marking them as containing errors (primarily, using CSS effects). Form component The core of Tapestry's form support is the Form|../tapestry-core/ref/org/apache/tapestry5/corelib/components/Form.html component. The Form component encloses (wraps around) all the other field components such as TextField|../tapestry-core/ref/org/apache/tapestry5/corelib/components/TextField.html, TextArea|../tapestry-core/ref/org/apache/tapestry5/corelib/components/TextArea.html, Checkbox|../tapestry-core/ref/org/apache/tapestry5/corelib/components/Checkbox.html, etc. The Form component generates a number of component events that you may provide event handler methods for.
[jira] Created: (TAP5-1189) Invoke JavaScript with LinkSubmit
Invoke JavaScript with LinkSubmit - Key: TAP5-1189 URL: https://issues.apache.org/jira/browse/TAP5-1189 Project: Tapestry 5 Issue Type: Question Components: tapestry-component-report Affects Versions: 5.1.0.5 Reporter: sakshi Hi, I am using Tapestry5. I have tried multiple options to invoke javascript on clicking linkSubmit, but none works, although they all work with Submit component of tapestry. a) Invoking javascript from tml page as follows: script function extraStep() { alert(ENTER extraStep); } /script t:loop source=photoDetails value=each -${each.title} br / t:form t:id=formId id=formIdp t:label for=reportedCategory/ t:select t:id=reportedCategory t:label=Reported Category//p [t:actionlink t:id=approvePhotosApprove/t:actionlink] input type=hidden id=photoObject value=${each} / t:linksubmit t:id=disapprovePhoto value=Disapprove Photo onclick=extraStep();/t:linksubmit /t:form /t:loop ** This works with Submit but not with linkSubmit b) tried using mixins, but didnt work with LinkSubmit c) tried using JSON, but that also didnt work with LinkSubmit. afterRender() does not get invoked. onSuccess(),onSelectedFromDisapprovePhoto() methods are not invoked from Linksubmit. Can someone please advise how can I use LinkSubmit in order to invoke javascript. Looking forward for an early response. regards sakshi -- This message is automatically generated by JIRA. - You can reply to this email to add a comment to the issue online.
[jira] Created: (TAP5-1190) New page-level events to decorate component event and page render links
New page-level events to decorate component event and page render links - Key: TAP5-1190 URL: https://issues.apache.org/jira/browse/TAP5-1190 Project: Tapestry 5 Issue Type: New Feature Components: tapestry-core Affects Versions: 5.2.0 Reporter: Howard M. Lewis Ship Priority: Minor This is based on some customer work. Customer really wanted URLs to include a series of (optional) values as query parameters, not path info ... which makes sense, because you'd have a category filter sometimes, a name filter sometimes, etc. Anyway, this worked fine in most cases ... there's a method on the page to act like the passivate event handler, but return a Link with the query parameters added. The active event handler would extract the query parameters and store them inside fields. Got trickier handling event links; had to modify some low-level components to fire a decorateLink event so that the page's event handler could add the query parameters to the link. It would be nice if these concepts were inside Tapestry; that after generating a Link via the passivate event (or by building a Link using a supplied page activation context) that an optional event (perhaps called decorateLink) would be triggered to add these extra query parameters. Ideally, there would be two events, decoratePageRenderLink and decorateComponentEventLink. The first parameter would be the Link to decorate. The second would be the PageRenderRequestParameters or ComponentEventRequestParameters, as appropriate. This would give the event handler method enough information to decide whether to decorate the link, and what information to put into it. -- This message is automatically generated by JIRA. - You can reply to this email to add a comment to the issue online.