| Recent Entries |
|
08. Mar 2010
|
|
|
03. Feb 2010
|
|
|
15. Jan 2010
|
|
|
12. Sep 2008
|
|
|
26. May 2008
|
In part 1 of this article, we learnt how to create a new Google App Engine project in Eclipse, integrate the Weld and JSF libraries, run the project locally and finally how to deploy it to the GAE production environment. This second part will look at some of the issues faced when developing a GAE application, particularly when coming from a Java EE development background.
First a disclaimer - while part 1 of this article was more a step by step guide, this part is more of a random collection of thoughts on various aspects of the GAE development process. I still consider myself quite a noob in this area, so if you think you have better information for any of the following topics please let us know in the comments area.
Let's get started! We'll begin by looking at one of the most important things, the persistence layer.
The Google App Engine datastore
If you're used to working with relational databases, then the GAE datastore might be a bit confusing at first. Although Google supports both JDO (don't ask me why) and JPA APIs for datastore access, you need to approach data access in GAE with a different mindset. The most important thing to remember is that the App Engine datastore is designed for scalability, not performance. As far as application architecture goes, my recommendation is to try and design your app to be based on simple, single-table queries filtered to return just the results you need. For data that seldom changes, I recommend that you avoid the database wherever possible and use a cached result instead (I'll cover this a bit later). To be a little bit more particular about what the datastore doesn't support, here's a brief list:
- Owned many-to-many relationships, and unowned relationships
- Join queries - you cannot filter by a child entity's field when querying its parent.
- Aggregation queries such as group by, sum, having, etc
Primary Keys
There are four different options available for primary key values. The easiest way is to just use a Long:
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) public Long getId()
However if you want to create a reference to your entity from other entities you should use an encoded String, which requires an additional annotation on the primary key field:
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true") public String getProjectId()
By using an encoded String primary key value, you can then create references using the standard JPA annotations:
@ManyToOne
@JoinColumn(name = "PROJECTID")
public Project getProject() {
return project;
}
Entity Relationships
By default, relationships to other entities defined via the @OneToOne or @ManyToOne annotations are lazy-loaded. This means that when you perform a query on the parent entity, the child entity isn't loaded until you explicitly read the child property from the parent. Obviously this is going to cause an extra round trip to the database, which may have performance implications. My suggestion here may be a little counter-intuitive, but in cases like this where you want to reduce calls to the database it may pay to de-normalize your database structure if possible.
Alternatively, you may wish to keep lookup data (for example records in COUNTRY or STATE tables in the case of address entities) cached, and simply store the primary key values of the lookup records in the parent table, rather than an object reference. It means a little more work, however is probably worth it for the performance benefits.
Creating the EntityManagerFactory
This is a very expensive operation, and should only be done once. The recommended way is to store the EntityManagerFactory instance in a static field, like this:
public final class EMF {
private static final EntityManagerFactory emfInstance = Persistence.createEntityManagerFactory("transactions-optional");
public static EntityManagerFactory get() {
return emfInstance;
}
}
To get EntityManager instances you can use a producer method. Since we don't support conversations in GAE yet (see the Unsupported features section below) the following code shows a request scoped producer:
public class EntityManagerProducer {
@Produces @RequestScoped EntityManager createEntityManager() {
return EMF.get().createEntityManager();
}
public void close(@Disposes EntityManager entityManager) {
entityManager.close();
}
}
Detaching entities
This one can be quite a gotcha. In GAE, managed entities may have hidden references back to certain database objects, such as the Query that loaded them. This causes problems with session serialization because those database objects aren't serializable. With JDO this isn't such a problem because it has a method called detachCopy() which detaches the object from the persistence context. Unfortunately the JPA spec has only recently introduced (in version 2.0) the detach() method which at present isn't available in GAE.
What this means, is that if you wish to cache objects you load from the database you either need to clone the entities in question, or alternatively create DTOs that contain only the properties that you wish to cache. Hopefully GAE will support JPA 2.0 in the future.
Indexes
Table indexes are configured in a file called datastore-indexes.xml in the WEB-INF directory. There is a property in this file called autoGenerate which if set to true is supposed to detect the queries that you execute when running the application locally and automatically create the necessary indexes. I found this a little flaky in practice and ended up having to configure some indexes manually, which is as simple as adding an entry to datastore-indexes.xml containing the fields of the index and their sort direction. Here's an example:
<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes autoGenerate="true">
<datastore-index kind="Comment" ancestor="false">
<property name="blogId" direction="asc" />
<property name="commentDate" direction="asc" />
</datastore-index>
</datastore-indexes>
Once your application is deployed to production, you can view the status of your indexes from the GAE dashboard, simply click the 'Datastore Indexes' link in the left hand column. Here's a heads up - index creation in GAE is SLOW! Even if your table contains no data whatsoever, it can literally take hours to create a simple index. So don't get worried if it seems like Google has forgotten to create your index, just be patient.
Security
A great feature of GAE is its integration with the Google Accounts API. In my opinion, why would you want to bother with having to create and maintain user and role tables, creating user registration views, 'I forgot my password' views, CAPTCHA, an e-mail facility for registration confirmation, etc when you can let Google do all that hard work for you. By using the Google Accounts API you allow anyone with a Google account to use your application, meaning you get all the advantages that come with being able to uniquely identify a visitor without any of the maintenance overhead.
Using the Google Accounts API to authenticate is a piece of cake. My recommendation is to create a simple Identity bean which takes care of the security-related stuff for you. Start by creating a method called getLoginUrl() which generates a URL that the user can click to authenticate:
@Named @SessionScoped
public class Identity {
public String getLoginUrl() {
ExternalContext ctx = FacesContext.getCurrentInstance().getExternalContext();
HttpServletRequest request = (HttpServletRequest) ctx.getRequest();
HttpServletResponse response = (HttpServletResponse) ctx.getResponse();
UserService userService = UserServiceFactory.getUserService();
return userService.createLoginURL(response.encodeUrl(request.getRequestURI()));
}
}
Once you've done that, you can then add the following code snippet to your page header or wherever to allow the user to sign into your application:
<ui:fragment rendered="#{not identity.loggedIn}">
<a href="#{identity.loginUrl}">Sign In</a>
</ui:fragment>
When the user clicks this link, they will be redirected to a Google Accounts sign in page. After entering a valid username and password, they will then be redirected back to your application as an authenticated user.
To sign out, you can add another method that generates a logout URL:
public String getLogoutUrl() {
UserService userService = UserServiceFactory.getUserService();
return userService.createLogoutURL("/");
}
For which the logout link would look like this:
<ui:fragment rendered="#{identity.loggedIn}">
<a href="#{identity.logoutUrl}">Sign Out</a>
</ui:fragment>
To get a reference to the current user, use the following method:
public User getCurrentUser() {
UserService userService = UserServiceFactory.getUserService();
return userService.getCurrentUser();
}
This gives you a reference to a com.google.appengine.api.users.User object, which has methods such as getEmail(), getNickname(), etc for retrieving certain information about the currently-logged in user. If you want to store a reference to a user in the database, use the String value returned by the getUserId() method. This method returns a long identifier value which is unique to that particular user. This is better than using their nickname or e-mail address, both of which can potentially change, whereas the user ID will never change.
Caching
GAE provides a feature called Memcache, which is a high performance distributed cache with generous daily limits. For a more detailed overview of Memcache, see here.
Memcache implements the javax.cache API, which is a good thing because it provides us with an API that you would use much the same way as you would use a Map. You place stuff into the cache using a familiar put(key, value) call, and get it out with a get(key) call. Simple huh?
The easiest way to get a reference to the cache is to just use a producer method. Here's one I prepared earlier:
import java.util.Collections;
import javax.cache.Cache;
import javax.cache.CacheException;
import javax.cache.CacheManager;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
public class CacheProducer {
@Produces @ApplicationScoped Cache getCache() throws CacheException {
return CacheManager.getInstance().getCacheFactory().createCache(Collections.emptyMap());
}
}
Once we're written this producer method we can simply inject the cache directly into a bean using @Inject Cache cache. We're going to look at caching a little more, further down in the performance section.
JSF
I don't want to get too deep into the JSF side of things because it's really outside the scope of this article. Basically JSF works as intended in GAE, with very little in the way of gotchas. Here's a couple of tips though if you're new to JSF 2.
Request parameters
Request parameters are now defined in the page itself, using the f:metadata tag. Simply use a f:viewParam to bind each request parameter to a property of your model:
<f:metadata>
<f:viewParam name="name" value="#{blogSearch.name}"/>
<f:viewParam name="start" value="#{blogSearch.start}"/>
</f:metadata>
If the parameter value isn't specified in the request then the value will be null, so make sure the property receiving the parameter value isn't a primitive (i.e. it must be nullable). Using request parameters are a great way of achieving bookmarkable URLs, and of developing a stateless application.
Page actions
This one may be a little strange to you if you're used to using Seam 2's pages.xml to define page actions. Like request parameters, in JSF 2 you also define page actions in the page itself (which kind of makes sense really). Simply use the f:event tag to define a preRenderView event, and specify the method you wish to invoke as the listener:
<f:event type="preRenderView" listener="#{blogAction.setup}"/>
Logging
My recommendation for logging is to use SLF4J. It's already included as part of the weld-servlet distribution so there's no extra libraries to add, and it's a piece of cake to use:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyClass {
Logger log = LoggerFactory.getLogger(MyClass.class);
}
Log messages created by SLF4J will show up in the GAE dashboard, to view them just click the 'Logs' link in the left hand column.
Performance
Let's get the important stuff out of the way first. When it comes to performance in GAE, there's a huge elephant in the room which is the JVM cold start issue that every low traffic application suffers from. To summarise the issue, if your application only receives minimal requests then it will spend most of its time in 'cold storage' (that's my term, but I give you permission to use it also). Once a loading request (this is what Google calls a request that initializes the application container) comes in for your application, GAE will start up a new container for your application to serve the request, and then after a set amount of time (can be just a minute or two) your application will be put back into cold storage again.
To contrast this with the way you're probably used to developing in Tomcat/JBoss/Glassfish (or whatever), a normal container will perform startup before any requests are served, meaning that the container is already hot when the first request comes in. In GAE however the container initialization itself is done during the request.
Why is this bad? Well, when you factor in that the basic container startup itself takes around 10 seconds, creating an EntityManagerFactory is around 3-4 seconds, JSF startup probably takes another few seconds and Weld itself can take 5-6 seconds (we're working at reducing this) we're looking at a minimum of 20 seconds or so just to serve that request. Often the request can even exceed the 30 second hard limit that GAE has, which means that the user is simply given an error message. If you're trying to attract new users to your site you can most likely see the problem here - most users aren't willing to wait even 3 seconds let alone 20+, and to give them an error message almost guarantees that they'll never be visiting your site again (unless it's your mother).
There is some hope though - Google are aware of this severe limitation and there is an open issue to address it:
http://code.google.com/p/googleappengine/issues/detail?id=2456
I strongly recommend you vote for this issue (by starring it) if you are thinking of using GAE for a low traffic site.
Now that I've talked about the big issue, let's look at some of the smaller stuff you can do to improve performance in your app. My number one tip is this - CACHE EVERYTHING! GAE's MemCache feature provides you a place to put your data which you can access much faster than performing a database lookup. If you've got certain queries that are executed on a regular basis then put the results in the cache and use that instead. The less database access you do in your app, the better.
Here's an example of caching query results:
private static final String CACHE_KEY = "RECENT_POSTS";
@Inject Cache cache;
public List<RecentPost> getRecentPosts() {
if (cache.get(CACHE_KEY) == null) {
List<RecentPost> recentPosts = new ArrayList<RecentPost>();
EntityManager em = entityManagerInstance.get();
List<Blog> results = em.createQuery("select b from Blog b order by b.entryDate desc")
.setMaxResults(5)
.getResultList();
for (Blog blog : results) {
recentPosts.add(new RecentPost(blog.getBlogId(),
userCache.getNameForUserId(blog.getUserId()),
blog.getTitle() != null && !"".equals(blog.getTitle()) ?
blog.getTitle() : "Untitled Post", blog.getTag()));
}
cache.put(CACHE_KEY, recentPosts);
}
return (List<RecentPost>) cache.get(CACHE_KEY);
}
By avoiding the database hit here, you'll make dramatic improvements to request times which in the end is one of the most important things for your users.
Another tip is to use paging whenever possible to constrain the size of your query results (when you're forced to query the database). Make use of the setMaxResults() and setFirstResult() methods provided by the Query API to limit how much data you present to the user in any single request.
Unsupported features
The most glaring omission in features is Weld's support for conversations. Due to the way that conversation cleanup is implemented in Weld (Future-based, which is a no-no in GAE) you currently get an exception when attempting to begin a new conversation. We will hopefully address this issue in a future release of Weld. My recommendation for now is to make your beans @RequestScoped wherever possible and model your application to be as stateless as possible. Use request parameters wherever it makes sense (see the JSF section above) and make use of the @Model stereotype (which when placed on a bean makes it @Named and @RequestScoped).
Conclusion
While we've covered a fair bit of ground in this article, there's probably a lot of other useful tips that I've missed. It may possibly be useful to convert this article (including Part 1) into a wiki page on seamframework.org which can serve as a central reference point for developing Weld apps in GAE, which anyone could contribute to. If you think this might be helpful and would like to see it happen, or have any other ideas please let us know in the comments.
Ok maybe it's not a minefield, but there's still quite a few gotchas when trying to build a Weld application to run in Google App Engine. Since I'm currently renovating my house I decided that it would be fun to blog about the experience (a software guy doing physical labour? weird!!) and it only made sense to kick the GAE tyres to see what all the hype was about. Google have done a great job providing a richly featured cloud platform with plenty of good documentation and a helpful online community, and the free quotas they provide are more than enough for the average low traffic web site.
If you are a typical Java EE developer like me though, you have to approach GAE development with a slightly different development mindset. This article will guide you through the step by step process for getting a Weld-based application running on GAE, and help you to avoid some speed bumps along the way. To get started, you'll need just 2 things - a JDK and Eclipse. For the rest of the stuff that you'll need I'll explain as we go along.
This article is rather large and as such will come in two parts; part 1 will cover project creation, configuration and deploying a minimal app to GAE, and part 2 will dive into some of the issues faced when developing an application.
Oh, by the way if you want to see an example Weld app running on GAE (and see me getting my hands dirty) then you can check out my site at http://www.renovatorsblog.com.
Downloading the Google App Engine SDK Eclipse Plugin
I consider the GAE SDK to be an essential ingredient for GAE app development. It handles all the work of setting up your project, enhancing your entity classes, running your app in local development mode and even deploying your app straight to the production GAE environment.
To install the SDK plugin for Eclipse, you can follow the instructions here:
http://code.google.com/appengine/docs/java/tools/eclipse.html
For the impatient though, here's a summary of the installation steps (for Eclipse 3.5):
- Click the Help menu -> Install New Software
- In the
Work with
box, enter http://dl.google.com/eclipse/plugin/3.5, then click the Add button then click OK. - Expand the Plugin entry and check the 'Google Plugin for Eclipse 3.5' item, then expand the SDKs entry and check the 'Google App Engine Java SDK 1.3.0' item.
- Click the Next button, and accept any terms of service.
- Click the Finish button, wait for installation to complete and then when asked if you want to restart Eclipse, select 'Yes'.
Create an App Engine account
If you don't have one already, then you'll need a GAE account. They're free, all you need is a Google account. So go to the following page and sign up:
Once you've done that, you should be presented with the 'My Applications' screen, containing an empty list and a 'Create an Application' button:
Create an Application
To be able to deploy an application to GAE, you need to give it a unique application identifier. Clicking the 'Create an Application' button will take you to the following screen, where you can enter your application identifier and a title for your application.
For the authentication options, you either have the choice of using Google Accounts which allows anyone with a Google account to sign in, or you can restrict your application just to the users of a Google Apps domain. We're going to choose door number one, and allow any user with a Google account to sign in.
Of course, if you don't want to then you don't have to require your users to sign in to your application at all (by not specifying any security restrictions for your pages, which we'll get to a bit later). If you do want some kind of security though, the advantage of using the Google accounts API is that you don't have to worry about building the screens, creating user/role entities, writing action code, etc for managing the users in your app - Google takes care of all that for you.
Creating the project
Once you've created a new application with a unique application identifier, you'll be presented with an 'Application Registered Successfully' message. Click the 'View the dashboard...' link to view the dashboard for your new app:
Now that we've created our new app, it's time to create the project. In Eclipse, click File -> New -> Web Application Project to get the following dialog:
Enter a project name and the default package for your classes. Uncheck the 'Use Google Web Toolkit' option (we'll be using JSF 2.0 for our view) and then click Finish to create the new project. Then, right click on the new project in the Package Explorer and select Google -> App Engine Settings.
In the 'Application ID' box, enter the unique application identifier that you registered for your application. For the Version, just enter 1, then click OK to save your settings. The values actually get saved in /war/WEB-INF/appengine-web.xml, so open this file up now and take a look at what it contains. Actually, there's one more setting you need to add to this file, to be able to support user sessions in your app. Add the following line to the file:
<sessions-enabled>true</sessions-enabled>
The complete file should look something like this:
<?xml version="1.0" encoding="utf-8"?> <appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> <application>myapplication</application> <version>1</version> <sessions-enabled>true</sessions-enabled> <!-- Configure java.util.logging --> <system-properties> <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/> </system-properties> </appengine-web-app>
Configuring JSF
Like any servlet-based application, the web.xml file is where most of the important stuff is configured. JSF requires quite a lot of config, so let's look at the options one at a time. By the way, Google actually have some documentation on configuring JSF 2.0 for GAE, so if you're interested you can check it out here.
The first thing we need to do is disable some of the multi-threaded stuff in JSF, as GAE doesn't support multi-threaded applications. Add the following two entries to /war/WEB-INF/web.xml:
<context-param>
<param-name>com.sun.faces.enableMultiThreadedStartup</param-name>
<param-value>false</param-value>
</context-param>
<context-param>
<param-name>com.sun.faces.enableThreading</param-name>
<param-value>false</param-value>
</context-param>
Next, we need to configure the Faces servlet. This will alllow requests that end in the .jsf suffix to be handled by JSF:
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.jsf</url-pattern>
</servlet-mapping>
Next, we need to configure EL:
<context-param>
<param-name>com.sun.faces.expressionFactory</param-name>
<param-value>com.sun.el.ExpressionFactoryImpl</param-value>
</context-param>
Finally, there's a couple more options recommended by Google. From what I've read, version 1.3.0 onward of the SDK now supports server-side state saving, however I haven't tried it yet so let's just leave it as client-side for now - the reason for this is to avoid a DatastoreTimeoutException issue that can possibly render a user's session broken until their session has expired (there's a Google Groups thread on this subject here if you're interested).
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
The following option is mentioned on the JSF 2.0 and GAE compatibility issues page, and is required to prevent a ConfigurationException from occurring because of a threading issue:
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Production</param-value>
</context-param>
Once you've added all these options to web.xml, you can also create a faces-config.xml file in the WEB-INF directory also. Since JSF 2.0 this file isn't actually required, but since we'll be putting our navigation rules there later on it won't hurt to create it now. Thankfully in JSF 2.0 there is minimal configuration required here, basically an empty file:
<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
</faces-config>
Adding the JSF libs to your project
Due to the way that JSF 2.0 checks if it can use InitialContext (see the processJndiEntries() method in WebConfiguration.java - there are certain Java classes that aren't supported in GAE, the ones that are can be found in the JRE whitelist), we need to use a hacked version of jsf-impl.jar that comments out the body of the processJndiEntries() method. You can download a modified jsf-impl.jar from Josh Carrier's blog, direct link here:
Once you download it, place it in the WEB-INF/lib directory in your project. You'll also need jsf-api.jar, which you can get directly from the following link. Once you get it stick it in the WEB-INF/lib directory also.
We'll also need to download the EL libraries. I couldn't really find an official download page just for EL, so just download them straight from the java.net Maven repository:
- http://download.java.net/maven/2/javax/el/el-api/2.2/el-api-2.2.jar
- http://download.java.net/maven/2/org/glassfish/web/el-impl/2.2/el-impl-2.2.jar
Once you download them both put them in the WEB-INF/lib directory.
Configuring Weld
The configuration for Weld is quite simple, all you need to do is add the following entry to web.xml:
<listener>
<listener-class>org.jboss.weld.environment.servlet.Listener</listener-class>
</listener>
Next, create an empty file (no contents whatsoever) called beans.xml in the /war/WEB-INF directory. This is so Weld can find your beans. Getting the Weld libs is a little more work, unfortunately. We've made a few fixes to the Weld code to address some issues with running in GAE, so you can't use the 1.0 download. The option here is to either a) build it yourself (which I'll go through the steps for in a moment, since you might be impatient ;) or b) wait for the 1.0.1.RC2 release, which should be out any day now.
Building Weld from SVN
Compiling the Weld libs from source is really not as scary as many people think, all you need is an SVN client and Maven 2. We only need the weld-servlet module, so start by checking it out from SVN:
svn checkout http://anonsvn.jboss.org/repos/weld/servlet/trunk/
Once you have it checked out, you can build it by running mvn clean install. Once it's built, copy the weld-servlet-xxx.jar file from the build/target directory into the WEB-INF/lib directory of your project. This is the only jar file you need to add to your project for Weld support.
With all the required libraries now in your project's WEB-INF/lib directory, do a refresh in Eclipse (either press F5 or right click on your project and select Refresh) then go into the Java Build Path page (right click on project -> Properties -> Java Build Path) and click on the Libraries tab. Click the 'Add Jars' button and browse to your project's war/WEB-INF/lib directory and select weld-servlet.jar, then click OK. This will allow us to use the CDI annotations in our classes.
Hello World
Now that we've got our project set up and all the required libraries in place, it's time to write some code. Just to ensure that everything is working ok, we're going to start with a simple Hello World example. First of all, create the following bean class in your new project:
import javax.inject.Named;
@Named
public class HelloWorld
{
public String getMessage()
{
return "Hello, World";
}
}
Next, create the following JSF page in the /war directory (call it helloworld.xhtml):
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<f:view xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
contentType="text/html">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
#{helloWorld.message}
</body>
</html>
</f:view>
Running your app locally
To take your new application for a test run it's best to try running it locally before you deploy it to production. Right-click on your project, then click Run As -> Web Application. You should see a few log messages as the servlet container starts up, and then the following message:
The server is running at http://localhost:8888/
Once you see this, it means your app is up and running. Open your web browser and go to http://localhost:8888/helloworld.jsf. If everything is in order you should see the following page:
If you see this page, then congratulations! It's time to deploy your app to the GAE production environment. If you don't see this page, then go through all the previous steps to make sure you haven't missed anything. After that, if you still can't get this page to display then post a comment describing where you're stuck and we'll try to help you out.
Deploying to Google App Engine
Now it's time to deploy your application. Right click on your project, and select Google -> Deploy To App Engine. You will be prompted for your Google accounts username and password, so enter them here. After entering them the deployment process will take a little while complete, but eventually you'll get a 'Deployment completed successfully' message.
To see if your deployment was successful, go to your web browser and open your application page - Google apps are deployed to <app-id>.appspot.com, where the app-id is your unique application ID. For example, if your application ID is joeblogshelloworld, then your application will be available at http://joeblogshelloworld.appspot.com. So open your application page as follows:
http://<app-id>.appspot.com/helloworld.jsf
If all has gone well, then you should see the same Hello, World message that you saw when running your application locally.
Summary
This concludes part 1 of this article, where we saw how to set up the GAE SDK, create a new GAE project, and configure JSF 2.0 and Weld. We also tested that our JSF page was working by evaluating an EL expression, and confirmed that our Weld bean could be found by using it as a backing bean for our page.
In the next part of this series, we'll spend some time looking at the application development process, and how to make use of the APIs that Google App Engine provides for security, database access and data caching, among other things.
As we get closer to a beta release of Seam Remoting 3.0, I'd like to talk a bit about what I think is one of the more exciting new features - the Model API. While chatting with Max in Antwerp last year, we were discussing the challenges associated with manipulating persistent objects remotely via AJAX. Dealing with lazy-loaded associations, detached entities, how to apply updates, etc are all issues faced when developing an AJAX-based user interface backed by an RPC-style API.
So it got me thinking. A view framework like JSF has an advantage in this area because it is component-centric, as opposed to RPC-centric. Each JSF control is typically bound to a component property somewhere in your server-side business model. When you invoke an action from JSF (e.g. submitting a form with a command button) any values that the user has entered into the form are (after validation) applied to the backing model (i.e. the update model phase), and then the action is invoked. Couple this with the extended managed persistence context provided by Seam and you have a powerful combination.
The Model API aims to provide a similar component-centric way of working with your server-side model, but using AJAX. To understand how it works, let's get right into the fun stuff and look at some code.
First of all we start by creating a model object (by the way this is client-side JavaScript):
var model = new Seam.Model();
So far so good. The model object is our gateway to the Model API. It provides methods for defining bean properties, fetching a model, expanding a model (I'll explain what this means later) and applying updates to a model. So the first step is to define the bean properties that we wish to work with. Let's say that we have a PersonAction bean that allows us to create new people, or edit existing people (this is server-side Java now):
public @ConversationScoped class PersonAction implements Serializable {
@PersistenceContext EntityManager entityManager;
@Inject Conversation conversation;
private Person person;
@WebRemote public void createPerson() {
conversation.begin();
person = new Person();
person.setAddresses(new ArrayList<Address>());
}
@WebRemote public void editPerson(Integer personId) {
conversation.begin();
person = entityManager.find(Person.class, personId);
}
@WebRemote public void savePerson() throws Exception {
if (person.getPersonId() == null) {
entityManager.persist(person);
} else {
person = entityManager.merge(person);
}
conversation.end();
}
public Person getPerson() {
return person;
}
}
As you can see, besides the @WebRemote annotations there's nothing special about this class. It is a @ConversationScoped bean because we need to manipulate its state across multiple requests. The Person entity is quite simple too - it contains a few basic properties and a lazy-loaded collection of Addresses:
@Entity public class Person implements Serializable {
private Integer personId;
private String firstName;
private String lastName;
private Date dateOfBirth;
private Collection<Address> addresses;
@Id @GeneratedValue public Integer getPersonId() { return personId; }
public void setPersonId(Integer personId) { this.personId = personId; }
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; }
public Date getDateOfBirth() { return dateOfBirth; }
public void setDateOfBirth(Date dateOfBirth) { this.dateOfBirth = dateOfBirth; }
@OneToMany(fetch = FetchType.LAZY, mappedBy = "person", cascade = CascadeType.ALL)
public Collection<Address> getAddresses() { return addresses; }
public void setAddresses(Collection<Address> addresses) { this.addresses = addresses; }
}
Configuring the model properties
Getting back to our client code, let's configure a bean property for our model. The
addBeanProperty() method takes three parameters - an alias, a bean class, and a property name.
In this example, the value contained in the person property of the PersonAction bean (i.e.
the value returned by PersonAction.getPerson()) will be made accessible under the local alias
of person
.
model.addBeanProperty("person", "org.jboss.seam.remoting.examples.model.PersonAction", "person");
After the model is fetched, the bean property value can be accessed via its alias, by using the getValue() method:
model.getValue("person")
Multiple bean properties may be configured any time before fetching the model - after the model has been fetched it is not possible to configure additional bean properties.
Fetching the model
To fetch the model we use the fetch() operation. When fetching a model, we may also specify an optional action. In this example let's pretend that we want to edit an existing person object, with a person ID of 42. We will need to invoke the PersonAction.editPerson() method to load the Person object from persistent storage. To do this we define an action like so:
var action = new Seam.Action()
.setBeanType("org.jboss.seam.remoting.examples.model.PersonAction")
.setMethod("editPerson")
.addParam(42);
Once the action is defined we can then fetch our model:
model.fetch(action);
When the server processes the fetch operation, it will first invoke the action method (if specified) and then marshal the model property values to send back to the client. In this example, the action method that we invoke has a call to conversation.begin() which begins a long-running conversation. The conversation ID is returned in the client response and the model automatically maintains a reference to it for subsequent requests.
After the model has been fetched we are free to modify the model properties however we like:
model.getValue("person").setFirstName("John");
model.getValue("person").setLastName("Smith");
Expanding the model
Remember how our Person entity has a lazy-loaded collection of Address objects? When the Person object is marshalled and sent to the client, the addresses property has a value of undefined. This is a very specific object state in JavaScript and is distinct from null.
What if we wish to work with the person's addresses though - for example to add a new address or modify an existing address? This is where model expansion can help us. The expand() method can be used to expand the model by loading uninitialized associations. In this case we wish to load the person's addresses and tack them onto our existing model. This is a piece of cake with the Model API - the expand() method accepts two parameters, the model property that contains the uninitialized value, and the name of the uninitialized property:
model.expand(model.getValue("person"), "addresses");
This operation will send a request that on the server will load the person's addresses, return the address objects back to the client and replace the previously uninitialized (i.e. undefined) addresses property with an initialized collection of addresses, which we can then modify however we want. Pretty cool huh?
Applying our changes
Once we have finished making changes to the model, we can then apply these changes by using the applyUpdates() operation. Like the fetch() operation we can also specify an optional action to invoke, but in contrast the action is invoked at the end of the request after first applying the model updates.
In this example, we wish to save the changes that we have made to the person (and its addresses) by invoking the PersonAction.savePerson() method. Once again, we create an action object defining the action we wish to invoke, and then pass it to the applyUpdates() method:
var action = new Seam.Action()
.setBeanType("org.jboss.seam.remoting.examples.model.PersonAction")
.setMethod("savePerson");
model.applyUpdates(action);
When the applyUpdates() operation is invoked, the client calculates a delta containing any differences between the original model values that were fetched, and the current values after being modified by the user. This is where most of the Model API magic happens - to summarize briefly though the client sends this delta to the server where it is applied to the appropriate managed entity instances before being flushed to the database.
Dynamic type loading
One other thing I should mention is another new feature of Seam Remoting - dynamic type loading. Previously, if you wished to work with certain object types in the client you were forced to import the JavaScript stubs for those objects, or else the Remoting API wouldn't recognize them. With dynamic type loading, when the Remoting API encounters one or more bean types that it doesn't recognize, it now makes a separate request to the server to fetch the metadata for those bean types. So in our examples above, when the client receives a model response containing references to Person and Address beans, the Remoting API transparently fetches the definitions for those types and then continues processing the original response without missing a beat.
Summary
To wind up, I hope that the Model API opens up some new possibilities for the interesting use of AJAX in dynamic web applications. Personally I've got some (at least I think so) pretty cool ideas for some new view layer related technology which will be based on this stuff, and it will be great to see what other people come up with also. You can play around with the Model API right now by checking out the Remoting module from SVN:
http://anonsvn.jboss.org/repos/seam/modules/remoting/trunk
Just be warned it's still in development, so please let me know if you find any issues. One thing that Seam doesn't have yet is transaction support for non-JSF requests (so the example that we looked at doesn't actually commit the transaction at the end) but we should have this shortly. Enjoy!
For those that are interested in what's happening with Seam Security, I've written an article describing the improvements to the security API in Seam 2.1.0. You can read the full article here, but to summarise, it covers the following features:
- The new Identity Management API
- The redesigned authorization system, including pluggable permission resolvers
- Persistent and rule-based permissions, and the Permission Management API
- Typesafe security annotations
The 2.1.0 release of Seam will also include JBossSSO and OpenID integration, as well as a bunch of other security improvements. It's really shaping up to be a huge release. Look forward to seeing another article describing how to use an OpenLDAP directory with Seam's Identity Management API shortly.