Why do I get an error about org.apache.log4j.Logger.isTraceEnabled()Z when launching an application in Jetty?

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]

Why do I get a ClassCastException when I pass a page or component to a service?

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]

Why Tapestry IoC? Why not use Spring or Guice?

This comes up too frequently. Spring and Guice are very good containers, but are targetted at defining services forapplications, notframeworks. 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]

Why do I have to inject a page? Why can't I just create one using 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]

Why is it necessary to pool pages? Couldn't they just be created fresh? Or stored in the HttpSession?

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]

Which is better, explict annotatations such as OnEvent or BeginRender or using the naming conventions, such as onAction() or beginRender()?

Both options have their place. In my experience, the naming convention option is very good for fast development, especially for prototyping. This is an area of great subjectivity, some developers will find the annotations more readable and maintainable, others will find the same for the naming convention approach.

Historically, the annotations came first, and the naming conventions were "overlayed" on top of them.

One advantage of using annotations, is that the method name can now be descriptive of what the method does (i.e., validateUserCredentials() instead of onValidateFormFromLogin()).

Annotations have a further advantage in that the attributes of the annotation can be constants, rather than literals:

                    private static final String LOGIN = "login";

                    @OnEvent(value=Form.VALIDATE_FORM, component=LOGIN)
                    void validateUserCredentials()
                    {
                    ...
                    }
               

This helps the compiler check your code for typos, and mnimizes the difficult of change when refactoring the names of components.

[top]

Should I use @Component or @InjectComponent?

They both have their uses. The basic reasoning is derived fromwhere the component type is defined.

Every component must have a specific type, a Java class that the framework can instantiate. This can come from a number of places:

  • When the component is defined in the template using a t:type element name, the type is defined by the element name.
  • When the component is a normal element, but has the t:type attribute, the type is defined by the attribute value.
  • When the component is a normal element and has the t:id attribute, there must be a corresponding @Component annotation to define the type.

The @InjectComponent annotation does not define the type of the component. The component type must be defined else where. The field that is annotated does not have to precisely match the component's actual type: it can be a super-class instead, or an interface implemented by the component.

[top]

Why are there both Request and HttpServletRequest?

Tapestry's Request interface is very close to the standard HttpServletRequest interface. It differs in just a few ways, omitting a number of unneeded methods, adding a couple of methods (such as isXHR()) and changing how a few of the existing methods operate. for example, getParameterNames() returns a sorted List of Strings; HttpServletRequest returns an Enumeration, which is a very dated approach.

However, the stronger reason for Request (and Response and Session, etc.) is for support of Portlets in the future. By writing your code in terms of Request, and not HttpServletRequest, you can be assured that the same code will operate in both Servlet Tapestry and Portlet Tapestry.

[top]