HiveMind Integration
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).
Injecting Services
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 |
You can access the service via the property. You can do this from a <binding> element, or from within the template, using an OGNL expression. For example: 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); . . . } }
Bootstrapping the Registry
The ApplicationServlet is responsible for initializing HiveMind's Registry on startup.
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:
- /WEB-INF/applicationId/hivemodule.xml
- /WEB-INF/hivemodule.xml
Both of these files exist in the web application context; the applicationId is the name of the application servlet, as given in web.xml deployment descriptor (this is only useful in the very rare case that you package more than one Tapestry application in a single web application).
By subclassing ApplicationServlet and overriding the constructRegistry() method, you can easily extend these rules, loading additional descriptors from arbitrary locations.