Tapestry 4.0 is intimately integrated into the HiveMind microkernel. Building a complex system onto a dependency injection microkernel such as HiveMind has many benefits; the code is easier to write, test and maintain. HiveMind's flexible approach makes it easy to provide extension points ... many of the common kinds of customizations in Tapestry 3.0 that required code changes (such as subclassing BaseEngine ) are now accomplished by providing objects via a HiveMind module descriptor for your application.
In fact, you should not think of HiveMind as just Tapestry's infrastructure, but as infrastructure for your application as well. A very succesful design pattern in Tapestry is to keep pages and components very simple, and delegate as much logic as possible out to HiveMind services. Listener methods should ideally do little more than marshall together the correct information and pass it over to a service (this sidesteps most of the issues with testing pages and components, which tend to be abstract).
Warning:Tapestry 4.0 is not compatible with HiveMind 1.0 . Tapestry 4.0 may only be used with HiveMind 1.1. The compatibility issues are related to the underlying Javassist library; HiveMind 1.0 and Tapestry 3.0 use one version of the library, HiveMind 1.1 and Tapestry 4.0 use a more recent version.
But how to get access to those services? Tapestry allows you to inject your pages and components with HiveMind services (or other objects accessible from within a HiveMind registry). This is accomplished via the <inject> specification element:
<page-specification class=". . ."> <inject property="mailSender" object="service:mymodule.MailSender"/> </page-specification>
This would create a new property on your page, mailSender, that would be connected to a HiveMind service, mymodule.MailSender. The object attribute is an object reference , consisting of a prefix ("service:") followed by a locator . The prefix identifies how the locator should be interpreted; in this case, as a full qualified service id. HiveMind itself defines a base set of prefixes , to which Tapestry adds the following:
Prefix | Description | Example |
---|---|---|
app-property |
The locator is the name of a property that is resolved using:
|
app-property:org.apache.tapestry.template-extension |
engine-service | The locator is the name of an engine service (an instance of IEngineService ). | engine-service:page |
global-property | The locator is the name of global property, defined as a servlet <init-parameter>, a servlet context <init-parameter>, or a HiveMind symbol. | global-property:org.apache.tapestry.disable-caching |
infrastructure | The locator is the name of a property provided by the tapestry.Infrastructure service; this service provides access to the key Tapestry services. | infrastructure:applicationSpecification |
ognl:mailSender.sendMail(to, subject)
would read the to and subject properties of the page, and pass them to the
sendMail() method of the mymodule.MailSender service (which has been injected
into the mailSender property).
From within Java code, you can define an abstract accessor method:
public abstract class MyPage extends BasePage { public abstract MailSender getMailSender(); . . . public void myListener(IRequestCycle cycle) { String to = getTo(); String subject = getSubject(); getMailSender().sendMail(to, subject); . . . } }
The ApplicationServlet will create a default registry, consisting of all META-INF/hivemodule.xml files found on the servlet classpath. This is how the base HiveMind and Tapestry module descriptors are loaded. You may package module deployment descriptors inside libraries or even in your application WAR.
In addition, two other descriptors will be parsed if they exist:
By subclassing ApplicationServlet and overriding the
constructRegistry()
method, you can easily extend these rules, loading additional descriptors from
arbitrary locations.