Here is a very lightweight and simple PersistenceFilter for your Guice powered webapps:
@Singleton
public class PersistenceFilter implements Filter {
private final Provider<PersistenceManager> pmp;
@Inject
public PersistenceFilter(Provider<PersistenceManager> pmp) {
this.pmp = pmp;
}
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
PersistenceManager pm = pmp.get();
try {
chain.doFilter(request, response);
} finally {
pm.close();
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
This is no replacement for warp-persist and the soon to come guice persistence support, but look how simple it is, I love it! :-)
So how does this work? the "magic" is done by using Guice scoping, basically PersistenceManager is tied to the Request scope and all we have to do is close it at the end of the request. I'm going to show this in the context of an AppEngine application (cuz thats what I'm working on right now), so here is our web.xml:
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<filter>
<filter-name>guiceFilter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>guiceFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>com.codeark.persistencefilterexample.Bootstrap</listener-class>
</listener>
</web-app>
Its an ordinary, run of the mill Guice powered webapp web.xml config file (see
this post if you're using wicket, and why...)
The Bootstrap listener looks like this:
public final class Bootstrap extends GuiceServletContextListener {
@Override
protected Injector getInjector() {
return buildInjector();
}
public Injector buildInjector() {
Injector infrastructure = Guice.createInjector(
new AppEngineModule(),
new PersistenceModule(),
new WebModule());
return infrastructure;
}
}
AppEngineModule handles the bindings of GAE specific services:
public class AppEngineModule extends AbstractModule {
private static final PersistenceManagerFactory pmf = JDOHelper.getPersistenceManagerFactory("transactions-optional");
public AppEngineModule() {
}
@Override
protected void configure() {
}
@Provides
PersistenceManagerFactory providesPersistenceManagerFactory() {
return pmf;
}
@Provides
UserService providesUserService() {
return UserServiceFactory.getUserService();
}
@Inject
@Provides
@SessionScoped
User providesCurrentUser(UserService userService) {
return userService.getCurrentUser();
}
}
Please note the
providesPersistenceManagerFactory() which localizes the use of static state to this module only. Additionally, diverging from the main topic of this post, take a look at
providesCurrentUser(UserService userService) which simply allows you to Inject the current Google Account User (if one is logged in this session) anywhere in your code, its really handy.
Now comes the PersistenceModule:
public class PersistenceModule extends AbstractModule {
public PersistenceModule() {
}
@Override
protected void configure() {
bind(PersistenceManager.class).toProvider(PersistenceManagerProvider.class).in(RequestScoped.class);
bind(DataService.class).to(DataServiceImpl.class);
}
}
The most important line here is the first one where we bind PersistenceManager to the request scope. The provider follows:
public class PersistenceManagerProvider implements Provider<PersistenceManager> {
private final PersistenceManagerFactory pmf;
@Inject
public PersistenceManagerProvider(PersistenceManagerFactory pmf) {
this.pmf = pmf;
}
@Override
public PersistenceManager get() {
return pmf.getPersistenceManager();
}
}
I could have used a @Provides method in PersistenceModule instead, but that would force me to inject PersistenceManagerFactory into PersistenceModule and I like to avoid module injection where possible.
Last but not least, WebModule:
public class WebModule extends ServletModule {
@Override
protected void configureServlets() {
filter("/*").through(PersistenceFilter.class);
serve("/test").with(TestServlet.class);
}
}
It is important that PersistenceFilter will precede any other filters or servlets that need persistence (see
Dispatch order in Guice documentation) otherwise you might end up with a closed persistence manager.
Finally, our test servlet which uses persistence via DataService:
@Singleton
public class TestServlet extends HttpServlet {
private final DataService dataService;
private final Provider<User> currentUser;
@Inject
public TestServlet(DataService dataService, Provider<User> currentUser) {
this.dataService = dataService;
this.currentUser = currentUser;
}
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setContentType("text/plain");
User current = currentUser.get();
if (current == null) {
resp.getWriter().println("You are not signed in to your google account, but Hello, world! anyways :-)");
} else {
resp.getWriter().println(String.format("Hello, %s", current.getNickname()));
//records user and ip in datastore
dataService.persist(new MyEntity(current, req.getRemoteAddr()));
}
}
}
The DataService implementation looks like this:
public final class DataServiceImpl implements DataService {
private Provider<PersistenceManager> pmp;
@Inject
public DataServiceImpl(Provider<PersistenceManager> pmp) {
this.pmp = pmp;
}
@Override
public <T> T persist(T instance) {
return pmp.get().makePersistent(instance);
}
@Override
public void delete(Object instance) {
pmp.get().deletePersistent(instance);
}
}
To summarize:
- PersistenceManager is bound to a Request Scope, each request has its own PersistenceManager instance.
- The PersistenceFilter is configured at the beginning of the request dispatch chain, its main role is to close the PersistenceManager at the end of each request.
- Data Aware classes like DataServiceImpl use PersistenceManager by means of a Guice Provider (direct injection of PersistenceManager will impose a scope restriction on when we can instantiate classes that use it, so I think its best to avoid it)
As explained at the beginning of the post, this implementation is good for prototyping and small projects, for large / complex projects I'd go with a more solid, tested and much more capable framework (like warp-persist and guice-persist when it comes out)
The source is available in the form of an eclipse project here:
You will need Google AppEngine Eclipse plugin to make it work.