Hi all,
I am using Tapestry for my web framework and Spring for the backend. I wanted a clean way to access the ApplicationContext from within Tapestry. I wasn't really happy with the method described in the Spring docs, so I came up with something else. I think it is a bit cleaner and perhaps faster. I thought I would share it. The key is to use Tapestry's concept of an "extension": a globally-available resource.
In my application, all backend functionality is exposed to the web tier via a "BookService" class. I had already configured a "bookService" bean in Spring. I wanted this bean available to my Tapestry Visit object as well as to pages/components. Here is my biblia.application file:
Code:
<application name="biblia" engine-class="com.pjungwir.biblia.web.MyEngine">
<description>Library</description>
<page name="Home" specification-path="Shelves.page"/>
<extension name="appContext" class="com.pjungwir.spring.contrib.MyApplicationContext"/>
</application>
You can see I'm creating an Extension of the MyApplicationContext class. That is a very simple class:
Code:
package com.pjungwir.spring.contrib;
import org.springframework.context.support.*;
public class MyApplicationContext extends ClassPathXmlApplicationContext {
public MyApplicationContext() {
super("applicationContext.xml");
}
}
My goal here was just to get a ClassPathXmlApplicationContext with a no-arg constructor. Indeed, it seems like such a constructor ought to already exist, since "applicationContext.xml" is a default filename in other parts of Spring. If this constructor were added, the MyApplicationContext class could just go away.
Finally, I overrode the createVisit method in the MyEngine class. I couldn't just specify the visit-class property, because I wanted to set the BookService when a Visit is created (that IOC thing....):
Code:
package com.pjungwir.biblia.web;
import com.pjungwir.biblia.BookService;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.engine.BaseEngine;
import org.springframework.context.ApplicationContext;
public class MyEngine extends BaseEngine {
private BookService _bookService;
public BookService getBookService() { return _bookService; }
public MyEngine() {
ApplicationContext ctx = (ApplicationContext)getSpecification().getExtension("appContext");
_bookService = (BookService)ctx.getBean("bookService");
}
protected Object createVisit(IRequestCycle cycle) {
Visit v = new Visit();
v.setBookService(getBookService());
return v;
}
}
The final change was that I had to start deploying my applicationContext.xml in WEB-INF/classes instead of just WEB-INF. This is because it was now getting loaded by an ClassPath loader instead of a Web loader.
Once I made these changes, I could access my BookService instance from the visit or as ognl
age.engine.bookService. Of course, if there are other beans in your ApplicationContext, it is easy to pull them out, too. You could even add a getApplicationContext method to your Engine class if you thought that would save you some typing.
Paul
__________________________
Pulchritudo splendor veritatis.