| <!-- $Id$ --> |
| <!-- |
| Copyright 2004 The Apache Software Foundation |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| --> |
| <chapter id="coding-components"> |
| <title>Designing new components</title> |
| <para> |
| Creating new components using Tapestry is designed to be quite simple. |
| </para> |
| <para> |
| Components are typically created through aggregation, that is, by combining existing |
| components using an HTML template and specification. |
| </para> |
| <para> |
| You will almost always want to define a short alias |
| for your new component in the application specification. This insulates developers from minor |
| name changes to the component specification, such as moving the component |
| to a different Java package. |
| </para> |
| <para> |
| Like pages, components should reset their state back to |
| default values when the page they are contained within is returned to the pool. |
| </para> |
| <para> |
| Most components do not have any state. A component which does should |
| implement the &PageDetachListener; interface, |
| and implement the <function>pageDetached()</function> method. |
| </para> |
| <para> |
| The <function>pageDetached()</function> method is invoked from the page's |
| <function>detatch()</function> method, which is invoked at the very end |
| of the request cycle, just before the page is returned to the page pool. |
| </para> |
| <section id="coding-components.base-class"> |
| <title>Choosing a base class</title> |
| <para> |
| There are two basic types of components: those that use an HTML template, and those that don't. |
| </para> |
| <para> |
| Nearly all of the base components provided with the Tapestry framework don't |
| use templates. They inherit from the class |
| &AbstractComponent;. Such |
| components must implement the protected <function>renderComponent()</function> method. |
| </para> |
| <para> |
| Components that use templates inherit from a subclass of |
| &AbstractComponent;: |
| &BaseComponent;. They should leave the <function>renderComponent()</function> |
| method alone. |
| </para> |
| <para> |
| In some cases, a new component can be written just by combining existing components |
| (this often involves using inherited bindings). Such a codeless |
| component will consist of just a specification and an HTML |
| template and will use the &BaseComponent; class without subclassing. This |
| is even more possible when using <link linkend="components.helper-beans">helper beans</link>. |
| </para> |
| </section> |
| <section id="coding-components.parameters"> |
| <title>Parameters and Bindings</title> |
| <para> |
| You may create a component that has parameters. Under Tapestry, component parameters are a |
| kind of "named slot" that can be wired up as |
| a source (or sink) of data in a number of ways. This "wiring up" is |
| actually accomplished using binding objects. |
| </para> |
| |
| <tip> |
| <title>Connected Parameters</title> |
| <para> |
| Most components use "in" parameters and can have Tapestry |
| <link linkend="components.connected-params">connect the parameters to properties |
| of the component</link> automatically. This discussion |
| reveals some inner workings of Tapestry |
| that developers most often |
| no longer need to be aware of. |
| </para> |
| </tip> |
| |
| <para> |
| The page loader, the object that converts a component specification into an actual |
| component, is responsible for creating and assigning the bindings. It uses the method |
| <function>setBinding()</function> to assign a binding with a name. Your |
| component can retrieve the binding by name using <function>getBinding()</function>. |
| </para> |
| <para> |
| For example, lets create a component that allows the color of |
| a span of text to be specified using a <classname>java.awt.Color</classname> object. The |
| component has a required parameter named <varname>color</varname>. The class's |
| <function>renderComponent()</function> method is below: |
| </para> |
| <informalexample> |
| <programlisting> |
| protected void renderComponent(&IMarkupWriter; writer, &IRequestCycle; cycle) |
| throws RequestCycleException |
| { |
| &IBinding; colorBinding = getBinding("color"); |
| Color color = (Color)colorBinding.getObject("color", Color.class); |
| String encodedColor = &RequestContext;.encodeColor(color); |
| |
| writer.begin("font"); |
| writer.attribute("color", encodedColor); |
| |
| renderWrapped(writer, cycle); |
| |
| writer.end(); |
| }</programlisting> |
| </informalexample> |
| <para> |
| The call to <function>getBinding()</function> is relatively expensive, since |
| it involves rummaging around in a ⤅ and then |
| casting the result from <classname>java.lang.Object</classname> to <classname>org.apache.tapestry.IBinding</classname>. |
| </para> |
| <para> |
| Because bindings are typically set once and then read frequently by the component, |
| implementing them as private instance variables is much more efficient. Tapestry |
| allows for this as an optimization on frequently used components. |
| </para> |
| <para> |
| The <function>setBinding()</function> method in |
| &AbstractComponent; checks to see if there is a read/write |
| JavaBeans property named "<replaceable>name</replaceable>Binding" of |
| type &IBinding;. In this example, it |
| would look for the methods <function>getColorBinding()</function> and <function>setColorBinding()</function>. |
| </para> |
| <para> |
| If the methods are found, they are invoked from |
| <function>getBinding()</function> and <function>setBinding()</function> instead of updating the |
| ⤅. |
| </para> |
| <para> This changes the example to:</para> |
| <informalexample> |
| <programlisting> |
| <emphasis>private &IBinding; colorBinding; |
| |
| public void setColorBinding(&IBinding; value) |
| { |
| colorBinding = value; |
| } |
| |
| public &IBinding; getColorBinding() |
| { |
| return colorBinding; |
| } |
| </emphasis> |
| |
| protected void renderComponent(&IMarkupWriter; writer, &IRequestCycle; cycle) |
| throws RequestCycleException |
| { |
| Color color = (Color)<emphasis>colorBinding</emphasis>.getObject("color", Color.class); |
| String encodedColor = &RequestContext;.encodeColor(color); |
| |
| writer.begin("font"); |
| writer.attribute("color", encodedColor); |
| |
| renderWrapped(writer, cycle); |
| |
| writer.end(); |
| }</programlisting> |
| </informalexample> |
| <para> |
| This is a trade off; slightly more code for slightly better performance. |
| There is also a memory bonus; the |
| ⤅ used by &AbstractComponent; to store the binding will never be created. |
| </para> |
| </section> |
| <section id="coding-components.persistent-state"> |
| <title>Persistent Component State</title> |
| <para> |
| As with pages, individual components may have state that persists between request cycles. |
| This is rare for non-page components, but still possible and useful in special circumstances. |
| </para> |
| <para> |
| A client that must persist some client state uses its page's <varname>changeObserver</varname>. |
| It simply posts <classname>ObservedChangeEvents</classname> with itself (not its page) as the |
| source. In practice, it still simply invokes the <function>fireObservedChange()</function> method. |
| </para> |
| <para> |
| In addition, the component should implement the interface |
| &PageDetachListener;, and implement |
| the method <function>pageDetached()</function>, and, within that method, reset all |
| instance variables to default values, just as a page does (in its <function>detach()</function> method). |
| </para> |
| </section> |
| <section id="coding-components.assets"> |
| <title>Component Assets</title> |
| <para> |
| Tapestry components are designed for easy re-use. Most |
| components consist of a specification, a Java class and an HTML template. |
| </para> |
| <para> |
| Some components may need more; they may have additional image |
| files, sounds, Flash animations, QuickTime movies or whatever. These are |
| collectively called "assets". |
| </para> |
| <para> |
| Assets come in three flavors: external, context and private. |
| </para> |
| <itemizedlist> |
| <listitem> |
| <para>An external asset is just a fancy way of packaging a URL at an arbitrary web site. |
| </para> |
| </listitem> |
| <listitem> |
| <para>A context asset represents a file with a URL |
| relative to the web server containing the Tapestry application.</para> |
| </listitem> |
| <listitem> |
| <para>A private asset is a file within the classpath, |
| that is, packaged with the component in a Java Archive (JAR) file. |
| Obviously, such assets are not normally visible to the web server. |
| </para> |
| </listitem> |
| </itemizedlist> |
| <para> |
| Components which use assets don't care what flavor they are; they |
| simply rely on the method <function>buildURL()</function> to provide a |
| URL they can incorporate into the HTML they generate. For example, the |
| &Image; component has an image parameter that is used to |
| build the <varname>src</varname> attribute of an HTML <sgmltag class="starttag">img</sgmltag> element. |
| </para> |
| <para> |
| Assets figure prominently into three areas: reuse, deployment and localization. |
| </para> |
| <para> |
| Internal and private assets may be localized: when |
| needed, a search occurs for a localized version, relative to a base name |
| provided in the component specification. |
| </para> |
| <para> |
| Private assets simplify both re-use and deployment. They allow a re-usable Tapestry |
| component, even one with associated images, style sheets (or other assets) to be incorporated |
| into a Tapestry application without any special consideration. For example, the standard exception |
| page makes use of a private asset to access its stylesheet. |
| </para> |
| <para> |
| In a traditional web application, private assets would need to be packaged |
| separately from the 'component' code and placed into some pre-defined |
| directory visible to the web server. |
| </para> |
| <para> |
| Under Tapestry, the private assets are distributed with the component |
| specification, HTML templates and Java code, within a Java Archive (JAR) |
| file, or within the <filename class="directory">WEB-INF/classes</filename> directory of a |
| Web Application Archive (WAR) file. The resources are located within the |
| running application's classpath. |
| </para> |
| <para> |
| The Tapestry framework takes care of making the private |
| assets visible to the client web browser. This occurs in |
| one of two ways: |
| </para> |
| <itemizedlist> |
| <listitem> |
| <para>The private assets are copied out of the |
| classpath and to a directory visible to the web server. |
| This requires some additional configuration. |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| The assets are dynamically accessed from the class |
| path using the asset service. |
| </para> |
| </listitem> |
| </itemizedlist> |
| <para> |
| Copying assets out of the classpath and onto the web |
| site is the best solution for final deployment, since it allows the assets |
| to be retrieved as static files, an operation |
| most web servers are optimized for. |
| </para> |
| <para> |
| Dynamically accessing assets requires additional operations in Java code. |
| These operations are not nearly as efficient as static |
| access. However, dynamic access is much more convenient during development |
| since much less configuration (in this case, copying of assets) is |
| necessary before testing the application. |
| </para> |
| <para> |
| As with many things in Tapestry, the components using assets |
| are blind as to how the assets are made visible to the client. |
| </para> |
| <para> |
| Finally, every component has an <varname>assets</varname> property that is an |
| unmodifiable ⤅. The assets in the |
| ⤅ |
| are accessible as if they were properties of the ⤅. In |
| other words, the property path <varname>assets.welcome</varname> is valid, if the |
| component defines an asset named 'welcome'. |
| </para> |
| </section> |
| </chapter> |