new?
Tapestry is dependent on a particular version of Log4J, one that adds the isTraceEnabled() method.
You need to get a copy of Log4J 1.2.14 and copy it into the Jetty
ext
directory. You should
delete the existing log4j.jar file.
| [top] |
Tapestry uses a special class loader for component classes. This includes pages, components, mixins and base classes (each in their own sub package).
As Tapestry loads the class, it transforms the class. This is to support the Tapestry page lifecycle, including pooling of pages. It also accounts for other things, such as persistent fields and parameters. This is also how Tapestry is able to invoke non-public event handler methods.
This means there are
two
versions of each class: the vanilla version and the Tapestry-tranformed version.
Inside a component,
this
refers to the transformed instance and the
transformed class. To the service layer, the type is the vanilla version. Same class name, different
java.lang.Class, and thus a ClassCastException.
The established technique is to define an interface that the component can implement. The parameter to the service layer method is the interface, not the component class.
| [top] |
This comes up too frequently. Spring and Guice are very good containers, but are targetted at defining services for applications, not frameworks. Tapestry has certain specific needs that are pervasive in the IoC layer, chief among them extensibility. It must be possible to override internal services on a spot basis. It must be possible to extend the configuration of an existing service.
These simply aren't concepts present in Spring and Guice. Spring can autowire by type or by explicit name. To support Tapestry's level of extensibility, each new Tapestry project would require a copy of a large Spring configuration file that would need to be customized. Adding an additional layer, such as tapestry-hibernate, would add additional configuration into the configuration file. This simply isn't the Tapestry way, where things Just Work (and where we avoid XML).
Guice is very similar; there's no XML, but there are marker annotations used to select a specific implementaton from a pool of objects with the same interface. This means that Java code would have to be replaced in some cases, to slip overrides into place.
Tapestry's service configuration concept is simply not present in other containers. The ability to extend existing service behavior by providing additional configuration is part of the light touch of Tapestry. Examples are elsewhere in this documentation.
| [top] |
new?
As explained elsewhere, Tapestry tranforms your class at runtime. It tends to build a large constructor for the class instance. Further, an instance of the class is useless by itself, it must be wired together with its template and its sub-components.
| [top] |
Tapestry pages tend to be quite large collections of objects. In the largely invisible structure around a page will be template objects, binding objects (the active parts of a component parameter), many injected services, and a lot of other structure besides.
Many of those objects are not serializable, and therefore, should not go into the HttpSession. In addition, many of the objects are shared between page instances, but serialization is like a deep copy and would create duplicates of such objects.
Finally, a relatively small number of page instances can support a much larger number of concurrent clients, as each page is only needed for a few milliseconds of work time. Even with users clicking buttons as fast as humanly possible, the majority of thier time is "think time" and there's no need to keep entire page instances waiting in the wings while they think.
It takes an appreciable amount of time to construct a working page instance, as all those objects need to be instantiated, looked up, organized and wired together. It's barely noticable to a single developer, but a real site with real traffic, would find it unnacceptible to create a new page instance for each request.
Further, Tapestry's structure allows for a lot of optimizations and caching. This means that the second use of a page tends to be more efficient than the first, as most cacheable values have been cached.
| [top] |
There are some odd edge cases involving inheritance and render phase methods. Here's an example:
<![CDATA[
public class BasePage
{
void beginRender()
{
System.out.println("BasePage -- beginRender()");
}
}
public class ChildPage
{
void beginRender()
{
System.out.println("ChildPage -- beginRender()");
}
}
]]> (sorry about the CDATA, it's a typical Maven bug, please ignore)Because there is a beginRender() method in the BasePage class, it will be invoked as part of the BeginRender phase. However, it is overridden by an identical ChildPage method. So the ChildPage method gets invoked once.
However, ChildPage also provides a beginRender() (an override of the parent class), so this method also gets invoked ... for a second time.
You can't turn off base class method invocations; what you can and should do is
make your event handler methods in a base class final.
| [top] |