blob: f7b949abe005f509fa6018ea9689db46859bd362 [file] [log] [blame]
<?xml version="1.0" encoding="UTF-8"?>
<faqs title="Frequently Asked Questions">
<part id="MavenIsAFuckingPieceOfShit">
<faq id="jetty-is-trace-enabled">
<question>Why do I get an error about org.apache.log4j.Logger.isTraceEnabled()Z when launching an
application in Jetty?
</question>
<answer>
<p>Tapestry is dependent on a particular version of Log4J, one that adds the isTraceEnabled() method.
</p>
<p>You need to get a copy of Log4J 1.2.14 and copy it into the Jetty
<code>ext</code>
directory. You should
delete the existing log4j.jar file.
</p>
</answer>
</faq>
<faq id="classcastexception">
<question>Why do I get a ClassCastException when I pass a page or component to a service?</question>
<answer>
<p>
Tapestry uses a special class loader for component classes. This includes pages, components, mixins
and base classes (each in their own sub package).
</p>
<p>
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.
</p>
<p>
This means there are
<em>two</em>
versions of each class: the vanilla version and the Tapestry-tranformed version.
Inside a component,
<code>this</code>
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.
</p>
<p>
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.
</p>
</answer>
</faq>
<faq id="why-not-spring-guice">
<question>Why Tapestry IoC? Why not use Spring or Guice?</question>
<answer>
<p>
This comes up too frequently. Spring and Guice are very good containers, but are
targetted at defining services for <em>applications</em>, not <em>frameworks</em>.
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.
</p>
<p>
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 <em>Just Work</em> (and where we avoid XML).
</p>
<p>
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.
</p>
<p>
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.
</p>
</answer>
</faq>
<faq id="page-lifecycle-and-new">
<question>
Why do I have to inject a page? Why can't I just create one using <code>new</code>?
</question>
<answer>
<p>
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.
</p>
</answer>
</faq>
<faq id="why-pool-pages">
<question>
Why is it necessary to pool pages? Couldn't they just be created fresh? Or stored in the HttpSession?
</question>
<answer>
<p>
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.
</p>
<p>
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.
</p>
<p>
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.
</p>
<p>
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.
</p>
<p>
Further, Tapestry's structure allows for a lot of optimizations and caching. This means that the
<em>second</em> use of a page tends to be more efficient than the first, as most
cacheable values have been cached.
</p>
</answer>
</faq>
<faq id="event-method-duplication">
<question>
Why are my methods getting invoked multiple times?
</question>
<answer>
<p>
There are some odd edge cases involving inheritance and
render phase methods. Here's an example:
</p>
<source><![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)</source>
<p>
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.
</p>
<p>
However, ChildPage also provides a beginRender() (an override of the parent class), so
this method also gets invoked ... for a second time.
</p>
<p>
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 <code>final</code>.
</p>
</answer>
</faq>
</part>
<!--
<part id="General">
<faq id="dead-doo-dad">
<question>My doo-dad is dead. How can I regenerate it?</question>
<answer>
<p>
Doo-dad generation happens at system boot. Therefore, the simplest
answer is to restart the hello-world-doodad service.
</p>
<p>
If your doodad service is widget-enabled, you can also regenerate
dead doo-dads using the /widgets/reincarnate.doodad address and the
following message data:
</p>
<source>
<reincarnation>
<id>1</id>
<password>ThisShouldBeEncrypted</password>
</reincarnation>
</source>
</answer>
</faq>
<faq id="what-is-a-doo-dad">
<question>What the h@!! is a doo-dad anyway?</question>
<answer>
<p>
Doo-dads are components of the hello-world system used to obfuscate
the misdirection server. It is critical to system health that the
doo-dad pool have an appropriate threshold and that it be culled
regularly.
</p>
</answer>
</faq>
</part> -->
</faqs>