| Recent Entries |
|
03. Aug 2010
|
|
|
16. Mar 2010
|
|
|
15. Mar 2010
|
|
|
08. Mar 2010
|
|
|
03. Feb 2010
|
|
|
15. Jan 2010
|
|
|
12. Sep 2008
|
|
|
26. May 2008
|
Just a heads up to everyone - I'll be updating the seamframework.org wiki software very shortly. Downtime is currently expected to be around 20 minutes, once the site is back up I'll post an update here.
Thanks for your patience!
Update: The site update didn't quite go as planned, with a last minute hiccup occurring with some obscure configuration issue. For now we're still running the previous version of the wiki software, and the upgrade is postponed temporarily. I'll keep this post updated with any further changes.
Update 2: The site has now been successfully upgraded. The purpose of this update was mainly to add a number of admin-only features to help us deal with the increasing problem of spam. You will now notice a new 'Report as spam' button within the wiki areas (e.g. knowledge base, FAQs) of the site. If you come across any comments that are obviously spam, it would be greatly appreciated if you could report this post to the site administrators by clicking the new button. If you come across any other issues while using the updated site, please report them here and we'll address them ASAP. Thank you once again for being patient.
I'm pleased to announce the availability of our first Seam 3 module, Seam Remoting 3.0.0.Beta1. This release provides a CDI-based port of most of the AJAX remoting features that you know from Seam 2.x, and also introduces a new feature, the Model API. Please refer to the following links for downloads and documentation, and if you encounter any issues please use the issue tracking link to let us know about them. (Seam Remoting now has its own project in JIRA).
Also, stay tuned for a release of the Seam XML module in the next day, which will allow you to configure your CDI beans via an XML configuration file.
As the title says, Seam 2.2.1.CR1 is now available for download - you can get it from Sourceforge here:
http://sourceforge.net/projects/jboss/files/JBoss%20Seam/2.2.1.CR1
Release notes can be found here:
And docs can be found here:
http://docs.jboss.org/seam/2.2.1.CR1
Enjoy!
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.
| Showing 1 to 5 of 8 blog entries |
|
|