Saturday, October 23, 2010

Failed trying to bake Spring into Vaadin + GAE + JDO

I'm doing web application development using Vaadin, Google App Engine, JDO / DataNucleus, and... Spring Framework.

Spring Framework actually works fine, except for the JDO part. And I'm not touching transaction management yet!

When getting PersistenceManagerFactory programmatically, the app works great.

public class PMF {

private static final PersistenceManagerFactory pmfInstance = JDOHelper
.getPersistenceManagerFactory("transactions-optional");

private PMF() {
}

public static PersistenceManagerFactory get() {
return pmfInstance;
}
}

Usage:

home.addComponent(new Button("View", new Button.ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
Collection<Organization> orgs = (Collection<Organization>) pm.newQuery(Organization.class).execute();
mainWindow.showNotification("You have " + orgs.size());
} finally {
pm.close();
}
}
}));

However, when using Spring Framework, it... sort of... doesn't work. I'll explain the "sort of" part later.

Here's part of my applicationContext.xml :

<bean id="persistenceManagerFactory"
class="org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean">
<property name="persistenceManagerFactoryName" value="transactions-optional" />
</bean>

And using it:

@Autowired
private PersistenceManagerFactory pmf;
...
home.addComponent(new Button("View", new Button.ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
PersistenceManager pm = pmf.getPersistenceManager();
try {
Collection<Organization> orgs = (Collection<Organization>) pm.newQuery(Organization.class).execute();
mainWindow.showNotification("You have " + orgs.size());
} finally {
pm.close();
}
}
}));

It's a simple change.

The app loads, and doing database operations on the Vaadin Application.init() works fine. However, trying to do database operations on a button click handler for example, throws the following exception:

com.vaadin.event.ListenerMethod$MethodException
Cause: javax.jdo.JDOFatalUserException: No available StoreManager found for the datastore URL key "". Please make sure you have all relevant plugins in the CLASSPATH (e.g datanucleus-rdbms?, datanucleus-db4o?), and consider setting the persistence property "datanucleus.storeManagerType" to the type of store you are using e.g rdbms, db4o
NestedThrowables:
org.datanucleus.exceptions.NucleusUserException: No available StoreManager found for the datastore URL key "". Please make sure you have all relevant plugins in the CLASSPATH (e.g datanucleus-rdbms?, datanucleus-db4o?), and consider setting the persistence property "datanucleus.storeManagerType" to the type of store you are using e.g rdbms, db4o
at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:507)
at com.vaadin.event.EventRouter.fireEvent(EventRouter.java:161)
at com.vaadin.ui.AbstractComponent.fireEvent(AbstractComponent.java:1154)
at com.vaadin.ui.Button.fireClick(Button.java:371)
at com.vaadin.ui.Button.changeVariables(Button.java:193)
at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.handleVariables(AbstractCommunicationManager.java:1094)
at com.vaadin.terminal.gwt.server.AbstractCommunicationManager.doHandleUidlRequest(AbstractCommunicationManager.java:590)
at com.vaadin.terminal.gwt.server.CommunicationManager.handleUidlRequest(CommunicationManager.java:266)
at com.vaadin.terminal.gwt.server.AbstractApplicationServlet.service(AbstractApplicationServlet.java:476)
at com.vaadin.terminal.gwt.server.GAEApplicationServlet.service(GAEApplicationServlet.java:242)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:806)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:58)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:122)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
at com.google.apphosting.utils.jetty.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:70)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:349)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:326)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:938)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:755)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
Caused by: javax.jdo.JDOFatalUserException: No available StoreManager found for the datastore URL key "". Please make sure you have all relevant plugins in the CLASSPATH (e.g datanucleus-rdbms?, datanucleus-db4o?), and consider setting the persistence property "datanucleus.storeManagerType" to the type of store you are using e.g rdbms, db4o
NestedThrowables:
org.datanucleus.exceptions.NucleusUserException: No available StoreManager found for the datastore URL key "". Please make sure you have all relevant plugins in the CLASSPATH (e.g datanucleus-rdbms?, datanucleus-db4o?), and consider setting the persistence property "datanucleus.storeManagerType" to the type of store you are using e.g rdbms, db4o
at org.datanucleus.jdo.NucleusJDOHelper.getJDOExceptionForNucleusException(NucleusJDOHelper.java:354)
at org.datanucleus.jdo.JDOPersistenceManagerFactory.freezeConfiguration(JDOPersistenceManagerFactory.java:544)
at org.datanucleus.jdo.JDOPersistenceManagerFactory.getPersistenceManager(JDOPersistenceManagerFactory.java:576)
at org.datanucleus.jdo.JDOPersistenceManagerFactory.getPersistenceManager(JDOPersistenceManagerFactory.java:557)
at com.abispulsa.bisnis.mobile.VaadinApp$1.buttonClick(VaadinApp.java:62)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.google.appengine.tools.development.agent.runtime.Runtime.invoke(Runtime.java:100)
at com.vaadin.event.ListenerMethod.receiveEvent(ListenerMethod.java:487)
... 35 more
Caused by: org.datanucleus.exceptions.NucleusUserException: No available StoreManager found for the datastore URL key "". Please make sure you have all relevant plugins in the CLASSPATH (e.g datanucleus-rdbms?, datanucleus-db4o?), and consider setting the persistence property "datanucleus.storeManagerType" to the type of store you are using e.g rdbms, db4o
at org.datanucleus.store.FederationManager.initialiseStoreManager(FederationManager.java:197)
at org.datanucleus.store.FederationManager.<init>(FederationManager.java:70)
at org.datanucleus.ObjectManagerFactoryImpl.initialiseStoreManager(ObjectManagerFactoryImpl.java:153)
at org.datanucleus.jdo.JDOPersistenceManagerFactory.freezeConfiguration(JDOPersistenceManagerFactory.java:526)
... 44 more

I'm really not sure what I did wrong... I also tried the alternative way of getting PersistenceManagerFactory per GAE's guide:

<bean id="persistenceManagerFactory" class="javax.jdo.JDOHelper" factory-method="getPersistenceManagerFactory">
<constructor-arg value="transactions-optional" />
</bean>

Result is exactly the same. I tried adding PersistenceManagerFactoryProxy and still the same error.

I tried adding a TransactionManager :

<bean id="persistenceManagerFactory"
class="org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean" autowire-candidate="false">
<property name="persistenceManagerFactoryName" value="transactions-optional" />
</bean>

<bean id="persistenceManagerFactoryProxy"
class="org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy">
<property name="targetPersistenceManagerFactory" ref="persistenceManagerFactory" />
<property name="allowCreate" value="true" />
</bean>

<bean id="transactionManager" class="org.springframework.orm.jdo.JdoTransactionManager">
<property name="persistenceManagerFactory" ref="persistenceManagerFactory" />
</bean>

still doesn't work.

I tried making the Spring PersistenceManagerFactoryProxy scope="session" and I get an exception saying it is not serializable (a common problem in Google App Engine development):

Oct 23, 2010 5:48:45 PM com.vaadin.terminal.gwt.server.GAEApplicationServlet service
SEVERE: NotSerializableException: java.io.NotSerializableException: org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy$PersistenceManagerFactoryInvocationHandler
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1164)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1518)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1483)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1518)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1483)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:330)
at java.util.HashMap.writeObject(HashMap.java:1000)
at sun.reflect.GeneratedMethodAccessor7.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:945)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1469)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1518)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1483)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1400)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:330)
at com.vaadin.terminal.gwt.server.GAEApplicationServlet.service(GAEApplicationServlet.java:248)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:806)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:58)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:122)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
at com.google.apphosting.utils.jetty.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:70)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:349)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:326)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:938)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:755)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)

Right now I'm stuck with Spring Framework and JDO.

There are several alternatives for me now:
  1. Leave JDO alone, use Spring for other parts of application
  2. Try another dependency injection framework, like Guice or Weld/CDI
  3. Keep banging my head...? :P

No comments:

Post a Comment