There is quite a bit of buzz about these IoC (Inversion of Control) containers of late. Spring is a popular and mature one out there. It just so happens that Tapestry comes bundled with its own called Hivemind. This is a lighter weight one than Spring but still manages to do some powerful stuff. There are a number of really nice things that HiveMind will do for you, many are demonstrated later.
A couple of excellent aspects are:
<service-point id="ExportService" interface="org.crud.services.IExportService"> <invoke-factory> <construct class="org.crud.services.ExportService"> <set-object property="linkFactory" value="service:tapestry.url.LinkFactory"/> <set-object property="applicationStateManager" value="service:tapestry.state.ApplicationStateManager"/> </construct> </invoke-factory> </service-point>
There are quite a few more things here of course so see: http://jakarta.apache.org/hivemind/services.html for more.
By default your web application should include a file:
src\main\webapp\WEB-INF\hivemodule.xml
...once again this will be automatically be included in the build.
Lets take a look into this:
<module id="crudmodule" version="1.0.0"> <!--Setup a Cayenne adapter so we can inject it into a page--> <service-point id="CayenneAdapter" interface="org.rz.squeezer.IDataObjectAdapter"> <invoke-factory> <construct class="org.rz.squeezer.DataObjectAdaptor"/> </invoke-factory> <!--<interceptor service-id="hivemind.LoggingInterceptor"/>--> </service-point> <!--Register the new squeezer with Tapestry--> <contribution configuration-id="tapestry.data.SqueezeAdaptors"> <adaptor object="service:CayenneAdapter"/> </contribution> <!-- Define the names and scope of ASO --> <contribution configuration-id="tapestry.state.ApplicationObjects"> <state-object name="visit" scope="session"> <create-instance class="org.crud.aso.Visit"/> </state-object> </contribution> <contribution configuration-id="tapestry.state.ApplicationObjects"> <state-object name="global" scope="application"> <create-instance class="org.crud.aso.Global"/> </state-object> </contribution> </module>
There are many things that can go on in the hive module. Here we setup a Tapestry to Cayenne squeezer. More on squeezers later, but essentially the default squeezer will Base64 a serializable object and put that on web pages, not the most efficient method but it is generic. What we've done here is to write a special squeezer just for Cayenne objects, all it does is take the primary key of the object, much more efficient (not that secure though). Once this code is declared for Hivemind to use you can pretty much forget about it and it just works in the background.
The second thing that is setup here are Application State Objects; one for each users session and one for the entire web application. The nice thing here is when using them inside a Tapestry page or component; all you have to do is "inject" them into your page and Hivemind takes care of the rest:
@InjectState("global") public abstract Global getGlobalObject(); @InjectState("visit") public abstract Visit getVisitObject();
Of course if you aren't running JDK5 you have no annotations but it is ok, you can just put a similar line into your page or component specification. However annotations are so much better as we can now just have a page superclass, make the above declarations once and then each sub-page can just call...
getVisitObject().getSomething();
...to work with each object inside your page or component. Tapestry and Hivemind do all the hard work!
Even without declaring anything in your Hive module you get a bunch of similar things practically for free:
Get another page in the application. Pages in Tapestry are pretty much just Java objects and values are passed between them by setters. So here we can get the page, set some values on it and call/invoke it.
@InjectPage("CrudList") public abstract CrudList getCrudListPage();
Get a component declared in the application.
@Component(type = "Border") public abstract Border getBorder();
Get a Bean. In this case a Bean class that helps with printing alternating row colours in CSS. Another common use is with validation delegates.
@Bean public abstract EvenOdd getRowClass();
Get an Engine service. Common use for a custom service is to stream some non-HTML data such as PDF or images to the user.
@InjectObject("engine-service:page") public abstract IEngineService getPageService();
@InjectObject("engine-service:remoteHost") public abstract String getRemoteHost();
and more...
@InjectObject("infrastructure:request") public abstract WebRequest getRequest();
@InjectObject("service:tapestry.globals.HttpServletRequest") public abstract HttpServletRequest getServletRequest();