If you are interested in Spring 3 on the GAE, there is a newer post about just this topic (and some REST!).
UPDATE: The github repository for this post is here. A prepared Eclipse project can be downloaded and imported into Eclipse with the installed Eclipse Google plugin (instructions below, to download a zipped project, press
on the github site).
Being really enthusiastic about Java on the Google App Engine, I tried out some simple examples, which I will document here.
Most interesting for me was the combination of the Google App Engine, GWT and – of course – the Spring Framework.
Since the current version of Spring is version 3.0, I tried this version, together with the new Google Eclipse plugin, which currently brings the App Engine SDK 1.2.1 and GWT 1.6.4.
The Eclipse version is Ganymede 3.4.2.
References
Most of this stuff is already described, I will list all useful sites here with a short summary – this post is just a mash-up of the mini-tutorials on these sites.
The first entry point for developing for the Google App Engine is obviously the documentation of Google itself. It is well written, easy comprehensible and there is a lot to find for this early stage.
- Always a useful helper is http://appengine-cookbook.appspot.com/, e.g. for this tipp, which was quite useful for me.
- The best introduction for the Google App Engine with Spring 3.0 topic is here.
- The best post about Spring with Google GWT is this (the gwtController below is taken from this post).
There are different other solutions described here, here and here. However, these solutions did not fit the demands of low impact, current version compatibility or just simple usage.
Preparation
- First, a “Google” Web Application is created by File > New > Web Application Project

Project setup
First, the added libraries.
- Libraries added to WEB-INF/lib and the to the project’s classpath
- Servlet jar is included in lib directory but not added to classpath

Besides the jar libraries, several configuration files and Java classes had to be included
- Java classes to be included or changed (detailed description below)
- Configuration files to be included or changed (detailed description below)

Function
The most innovative part is taken from here and described in detail.
The following parts have been added:
- The EMF class was made a spring component, it is created by Spring’s factory-method.
- The context:component-scan is used for autowiring.
- The JpaTemplate is used for simplified resource management.
Below are the missing parts.
EMF.java
package com.commons.server;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.springframework.stereotype.Component;
@Component
public class EMF {
private static final EntityManagerFactory emfInstance =
Persistence.createEntityManagerFactory("transactions-optional");
public EMF() {}
public EntityManager entityManager() {
return emfInstance.createEntityManager();
}
}
persistence.xml
<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<persistence-unit name="transactions-optional">
<provider>org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider</provider>
<class>com.commons.server.Customer</class>
<properties>
<property name="datanucleus.NontransactionalRead" value="true"/>
<property name="datanucleus.NontransactionalWrite" value="true"/>
<property name="datanucleus.ConnectionURL" value="appengine"/>
</properties>
</persistence-unit>
</persistence>
Customer.java
package com.commons.server;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
GreetingsServiceImpl.java
package com.commons.sigae.server;
import java.util.Collection;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.PersistenceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.jpa.JpaCallback;
import org.springframework.orm.jpa.JpaTemplate;
import org.springframework.stereotype.Component;
import com.commons.sigae.client.GreetingService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
@SuppressWarnings("serial")
@Component
public class GreetingServiceImpl extends RemoteServiceServlet implements GreetingService {
private boolean first;
protected EntityManager entityManager;
@Autowired
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
public JpaTemplate getTemplate() {
return new JpaTemplate(entityManager);
}
public String greetServer(String input) {
return getData(input);
}
private String getData(String input) {
String all = "";
for (Customer customer : getCustomers()) {
if (!first) {
removeCustomer(customer);
}
}
first = true;
for (Customer customer : getCustomers()) {
all += "id: " + customer.getId() + " - firstname: " + customer.getFirstName() + " - name:" + customer.getLastName();
}
createCustomer(input);
return all;
}
private void removeCustomer(Customer customer) {
EntityTransaction trx = entityManager.getTransaction();
trx.begin();
entityManager.remove(customer);
trx.commit();
}
private void createCustomer(String input) {
EntityTransaction trx = entityManager.getTransaction();
Customer newCustomer = new Customer();
newCustomer.setFirstName(input + System.currentTimeMillis());
newCustomer.setLastName(input + System.currentTimeMillis());
trx.begin();
entityManager.persist(newCustomer);
trx.commit();
}
private Collection<Customer> getCustomers() {
return getTemplate().execute(new JpaCallback<Collection<Customer>>() {
@Override
public Collection<Customer> doInJpa(EntityManager arg0) throws PersistenceException {
return (Collection<Customer>) entityManager.createQuery("SELECT cust FROM com.commons.sigae.server.Customer cust").getResultList();
}
});
}
}
dispatcher-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.commons" />
<bean id="entityManager" factory-bean="EMF" factory-method="entityManager" />
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>
/springingae/greet=gwtController
</value>
</property>
</bean>
<bean name="gwtController" class="com.commons.sigae.server.GWTController">
<property name="remoteService" ref="greetingServiceImpl"/>
</bean>
</beans>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<!-- Default page to serve -->
<welcome-file-list>
<welcome-file>SpringInGAE.html</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/springingae/greet</url-pattern>
</servlet-mapping>
</web-app>
Tipps & Tricks:
June 22, 2009 at 8:18 pm
Very usefull, thank you… but I have a question: Can I send jpa objects to the client?
June 22, 2009 at 11:38 pm
Hi Edwin,
phew, I played around and came across several problems, all related to Serialisation issues:
1. Serializable or IsSerializable must be implemented and
2. an empty constructor must be provided
3. Customer had to be moved to the client package
…but there I am stuck, this seems to be a common problem: the JPA class is enriched with jdoDetachedState, which is an Object array and this cannot be serialized.
Compare this post:
http://code.google.com/p/google-web-toolkit/issues/detail?id=3279#c4
which imho also provides the best (and most painful) solution (Comment 3): creating data transfer objects for the JPA ones – this fits best the bridge between Java and JavaScript.
However, there might be easier solutions (I am definitely not an expert in GWT) which I would be glad to see (eg. influencing the creation of the jdoDetachedState or changing the type to Serializable[] etc.)
Best,
ice09
January 11, 2010 at 5:03 am
return (Collection) entityManager.createQuery(“SELECT cust FROM com.commons.sigae.server.Customer cust where firstname = ‘test’”).getResultList();
are you able to execute statementl like above in gae? i get zero result
January 11, 2010 at 8:53 pm
Hi asianCoolz,
you have to use the cust prefix and firstName instead of firstname, then it works.
January 11, 2010 at 1:17 pm
agreed. can you please help try SELECT cust FROM com.commons.sigae.server.Customer cust where cust.firstname = ‘test’ on your testapp and post your result? I have strange output of zero and checking any other developer having same problem
January 11, 2010 at 8:52 pm
Hi asianCoolz,
yes, it works for me, I just removed the timestamp from the createCustomer Method and changed the Statement to
SELECT cust FROM com.commons.sigae.server.Customer cust where cust.firstName=’test’
Please note the cust.firstName (so prefix ‘cust’ and firstName camelcased). This works fine for me.
January 13, 2010 at 11:28 am
ya it work.
how about
SELECT cust FROM com.commons.sigae.server.Customer cust where cust.firstName like ’t%’
let say you have 18 records with username like ‘test’,'test’…until ‘test’ (18 times) . does it list all ? i able to get the result, but it is less than correct result suppose to be 18 instead, i get 7.
can try this?
January 13, 2010 at 12:56 pm
You can use the Datastore Viewer for this test, please see here, it is now available locally and in the remote app engine:
http://stackoverflow.com/questions/1102449/how-to-browse-local-java-app-engine-datastore
January 20, 2010 at 6:31 am
Is it possible to connect to some other database this way? I would like to know, if I can use MySQL or Oracle from local App Engine?
January 22, 2010 at 1:28 pm
Hi Gnuce,
no, this is not possible, Google App Engine does not support relational databases.
However, you can use abstractions like JPA or JDO.
January 22, 2010 at 1:36 pm
@Gnuce: it wouldn’t make a lot of sense to use Oracle or MySQL from a local App Engine if App Engine doesn’t support it. Using App Engine makes just sense if you actually want to upload your server and host it there.