Your browser may have trouble rendering this page. See supported browsers for more information.

This page shows the source for this entry, with WebCore formatting language tags and attributes highlighted.

Title

When Tapestry's @Inject* Silently Fails

Description

<n>This article was originally published on the <a href="http://blogs.encodo.ch/news/view_article.php?id=23"><b>Encodo Blogs</b></a>. Browse on over to see more!</n> <hr> Most Tapestry programming involves writing event handlers and operations on page objects. In order to execute these operations, you need access to properties of the form and properties of the session and application in which the page resides. For convenience, developers can add references to all sorts of objects in the system using various forms of the <c>@Inject*</c> annotation (like <c>@InjectPage</c>, <c>@InjectObject</c> and so on). Pages are declared abstract and, when instantiated, Tapestry extends the abstract class to fill in all of these injected objects and maintain proper initialization and linkage with rest of the system. As long as this works as expected, there's no problem. However, when Tapestry can't inject a property as specified, it fails silently instead of throwing an exception. Surely, the failure is logged somewhere, but turning on logging results in a flood of output that is all-too-quickly overwhelming. If an object cannot be injected, there should be an exception---else why would the page have tried to inject it? Because it would <i>appreciate</i> access to that object, but only if it's not too much trouble? <h>Injecting and Overrides</h> Injection does not work if the property to inject also happens to override an existing method or implements an interface method. The example below shows what to watch out for: Given the following interface: <code>interface IEditorInterface { IPage getEditorPage(); }</code> If a page class implements this interface in the following way, by tring to get Tapestry to inject an implementation (which would be quite elegant), the page reference will sometimes return null and sometimes generates a duplicate declaration error caused by a race condition (see <a href="https://issues.apache.org/jira/browse/TAPESTRY-846" title="Random error accesing pages: Property ' ' has already been accounted for by the element at Annotation ...">Random error acces[s]ing</a> for more information). <code>public abstract class EditorPage extends BasePage implements IEditorInterface { @InjectPage("Editor") public abstract Editor getEditorPage(); }</code> As mentioned above, this would be quite elegant, but it doesn't function reliably at all. In the case of <c>@InjectPage</c>, it is quite shaky, whereas properties declared as <c>abstract</c>, which are automatically implemented and managed by Tapestry, are always null or result in runtime abstract errors (class instantiated, but method not implemented). To get around this problem, use two separate methods, one to implement the interface and the other to inject the object: <code>public abstract class EditorPage extends BasePage implements IEditorInterface { public IPage getEditorPage() { return getInjectedEditorPage(); } @InjectPage("Editor") public abstract Editor getInjectedEditorPage(); }</code> In the above example, the function name for the injected page was changed; it is also possible to adjust the interface to use a more specific name, like "getEditorInterfacePage". This solution lets the class use the name "getEditorPage" for the more specific type (returning ''Editor'' instead of ''IPage''). <n>Using Java 1.5, Tapestry 4.02, Hivemind 1.1.1</n>