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=EventConstants.VALIDATE_FORM, component=LOGIN)
                    void validateUserCredentials()
                    {
                    ...
                    }
               

This helps the compiler check your code for typos, and minimizes 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]

Why do I get an exception about invoking a constructor when I use my bean with the BeanEditForm component?

When you build a UI using BeanEditForm, the BeanEditForm component needs a place to store the data that is provided in the submission.

If you don't provide a new instance, it will instantiate one.

The BeanEditForm component uses the same mechanism that is used to instantiate services in the Tapestry IoC container; it looks for the constructor with the most parameters, and tries to match each parameter to an IoC service.

When you have a constructor like: public MyBean(String name, Date date), Tapestry will try to inject the date parameter and find no IoC service matches that type.

The solution is to put an @Inject annotation on the constructor Tapestry should be using (the default constructor, with no parameters).

Alternately, provide a handler for the prepareForSubmit event, and store an instance into the property connected to the BeanEditForm. The BeanEditForm will see a non-null value and use it, therefore it will not get tripped up in terms of instantiating an instance.

[top]

How do I get Hibernate to startup up when the application starts up, rather than lazily with the first request for the application?

This really can bother people with very large Hibernate schemas, as Hibernate can take considerable time to start up. It would be better if Tapestry and Hibernate initialized early, before accepting any requests.

The trick is to force the Tapestry services that wrap around Hibernate to initialize at startup. The following can be added to your AppModule:

                    public void contributeRegistryStartup(OrderedConfigurationRunnable configuration, final
                    HibernateSessionSource sessionSource)
                    {
                    configuration.add("HibernateStartup", new Runnable()
                    {
                    public void run()
                    {
                    sessionSource.getConfiguration();
                    }
                    });
                    }
               
[top]