blob: efa02c632387414fdaa9a72fbb990d03a1c56b9f [file] [log] [blame]
<!-- $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="beans">
<title>JavaBeans and Properties</title>
<para>
The Tapestry framework is based upon the use of
<ulink url="http://java.sun.com/j2se/1.3/docs/api/java/beans/package-summary.html">JavaBeans</ulink>
and JavaBeans properties. This chapter is a short review
of these concepts. A more
involved discussion is available as part of the
<ulink url="http://java.sun.com/docs/books/tutorial/javabeans/index.html">Java Tutorial</ulink>.
</para>
<section id="beans.beans">
<title>JavaBeans</title>
<para>
The JavaBeans framework is a way of manipulating Java objects when their exact type is not known. The
ability to make objects work together, when their exact type is not known, is very powerful. It's an example
of the kind of flexibility availble in a highly dynamic language like Java that is not possible in
lower-level languages such as C++.
</para>
<para>
The JavaBeans framework is the basis for a number of component-based frameworks, including Java's AWT and Swing GUI
libraries, as well as Tapestry. The idea is that, by following a few naming rules and coding
conventions, it is possible to "plug into" a framework with new classes, classes not even written yet
when the framework is created. In Tapestry terms, this is used to allow the creation of new Tapestry components.
</para>
<para>
Any Java object can act as a JavaBean; it just has to follow certain naming conventions (discussed in
<link linkend="beans.properties">the next section</link>). In cases where a framework needs to create new
instances of a class, such as when Tapestry creates a new instance of a component,
the Java class must implement a public, no arguments constructor (it may implement additional constructors as
well).
</para>
<para>
The <ulink url="http://java.sun.com/j2se/1.3/docs/api/java/lang/reflect/package-summary.html">Java Reflection API</ulink> allows Tapestry to access the methods, attributes and constructors of a class.
</para>
</section>
<section id="beans.properties">
<title>JavaBeans Properties</title>
<para>
For Tapestry, the central concept for JavaBeans are properties. The JavaBeans framework allows Tapestry to
treat any object as a collection of named properties. Tapestry frequently reads, and occasionally writes, values
from or to these named properties.
</para>
<para>
A property is <emphasis>not</emphasis> the same as an <emphasis>attribute</emphasis> ... though, most often, each property is backed up by an attribute.
To Tapestry, and the Reflection API, a property is a set of public methods on the object.
Accessing a property involves invoking one of these methods.
</para>
<example>
<title>JavaBeans getter method</title>
<programlisting>
public <replaceable>type</replaceable> get<replaceable>Name</replaceable>()
{
...
}
</programlisting>
</example>
<example>
<title>JavaBeans setter method</title>
<programlisting>
public void set<replaceable>Name</replaceable>(<replaceable>type</replaceable> value)
{
...
}
</programlisting>
</example>
<para>
A property may be read-only or write-only (that is, it may implement just one of the
two methods). The <replaceable>type</replaceable> may be a scalar type (boolean, int, etc.)
or any Java class.
</para>
<para>
Note the naming; the first letter of the property name is capitalized after <literal>get</literal>
or <literal>set</literal>. JavaBeans properties are case sensitive with respect to the method names
and the property names. A special case exists when the name is an acronyn; this is recognized
by two or more upper-case letters in a row (after get or set); in this case, the property name
does <emphasis>not</emphasis> have the first letter convert to lower-case.
</para>
<para>
As a special case, a boolean property may use an alternate name for the getter method:
</para>
<example>
<title>JavaBeans getter method (boolean)</title>
<programlisting>
public boolean is<replaceable>Name</replaceable>()
{
...
}
</programlisting>
</example>
<para>
Although the normal implementation is to get or set an instance variable, more complicated options are
possible. One pattern is <emphasis>lazy evaluation</emphasis>, where an expensive calculation is
put off until the actual value is needed, for example:
</para>
<example>
<title>Lazy evaulation of JavaBeans property</title>
<programlisting>
public List userNames = null;
/**
* Returns a List of user names obtained from the database.
*
**/
public List getUserNames()
{
if (userNames == null)
userNames = fetchUserNamesFromDatabase();
return userNames;
}
</programlisting>
</example>
<para>
Here, the first time the method is invoked, the expensive database fetch occurs. The value
returned from the database is then cached for later invocations.
</para>
<para id="beans.properties.synthesized">
Another common pattern is a <emphasis>synthesized property</emphasis>. Here, there is no
real attribute at all, the value is always computed on the fly. A frequent use of this is to
avoid tripping over null pointers.
</para>
<example>
<title>Synthesized JavaBeans Property</title>
<programlisting>
/**
* Returns the name of the company's account representative, if
* if the company has one, or null otherwise.
*
**/
public String getAccountRepName()
{
AccountRep rep = company.getAccountRep();
if (rep == null)
return null;
return rep.getName();
}
</programlisting>
</example>
<para>
This example creates a synthetic property, <varname>accountRepName</varname>.
</para>
</section>
<section id="beans.property-path">
<title>Property Paths</title>
<para>
The JavaBeans framework provides basic named properties for JavaBean objects. Tapestry
extends this from simple properties to <emphasis>property paths</emphasis>.
</para>
<para>
A property path is a series of property names, separated by periods. When reading a property path, each
property is read in series.
</para>
<para>
In the <link linkend="intro.ex">example from the introduction</link>, the property path <varname>visit.userName</varname>
was referenced. This path means that the <varname>visit</varname> property of the start object (a Tapestry page) should be accessed, then the <varname>userName</varname> property of the visit object should
be accessed. This is approximately the same as Java code
<literal>getVisit().getUserName()</literal> (except that property access is not typesafe).
</para>
<para>
In some cases, property paths are used to change a value, instead of reading it. When this occurs, all properties
but the last a read; only the last property is written. In other words, updating <varname>visit.userName</varname>
would be similar to the JavaCode <literal>getVisit().setUserName(<replaceable>value</replaceable>)</literal>.
</para>
<para>
Property paths can be of any length; however, they are just as susceptable to <classname>NullPointerException</classname>s
as any other JavaCode. Care must be taken that none of the properties in a property path,
except the final one, return null. This can often be accomplished
using <link linkend="beans.properties.synthesized">synthesized properties</link>.
</para>
</section> <!-- beans.property-path -->
<section id="beans.ognl">
<title>Object Graph Navigation Library</title>
<para>
Beyond even simple property paths are the powerful Object Graph
Navigation Library (OGNL) <emphasis>expressions</emphasis>.
OGNL expressions are modeled after Java expressions; they can
invoke methods, perform comparisons, do arithmetic ... even build collections on the fly.
</para>
<para>
OGNL is a separate framework from Tapestry; further details about it
are available at
<ulink url="http://www.ognl.org">http://www.ognl.org</ulink>.
</para>
</section> <!-- beans.ognl -->
</chapter>