"GAE"(Google App Engine)在早些時候發布了Java語言的支持,對廣大的Java開發者來說,可以使用自己熟悉的語法來進行開發,是很舒服的一件事情。Java世界中有大量已有的工具和框架,有一些是我們日常工作中就已經熟練使用的、而且廣為流行的成熟的,如果能夠直接在GAE中使用,將大大提高我們的開發效率。
這幾天利用Struts2,在GAE的Java版本支持環境中,寫了個簡單博客,現在將GAE+Struts2的組合經驗,記錄下來。
背景介紹
Python是一門有趣的語言,是作者第二個接觸的動態腳本語言。第一個是大學時期做學生網站時期接觸到Perl,後來工作後利用Perl改寫了公司的應用程序啟動框架;幾年後,由於Perl的語法實在是太過於羞澀難懂,便使用Python對啟動框架進行了一次升級。Python是面向對象的,同時也兼備了函數式編程的支持,另外,Python的語法強制縮進,非常容易讀懂,因此Python版本的啟動框架對公司的Java開發者來說,也不難維護。
當然,這些都是題外話,GAE最先提供的是Python語言的支持,但是對我們Java開發者來說,要使用Python來進行大的應用開發,還比較痛苦的一件事情。這時候GAE推出Java語言的支持,既是所有Java開發人員的福音,也是理所當然的一件事情,Google不可能放棄目前企業應用開發領域裡面最大的一股力量。
Struts2是目前應用最廣泛的WEB開發框架,也是大部分的Java開發者最熟悉的開發框架,我們的GAE應用程序中,使用成熟的Struts2可以減少很多額外的開發工作。
所需的Struts的庫文件
目前Struts2的穩定版本是2.1.6,將下面的來自Struts-2.1.6的發行包的幾個包,引入你的GAE項目工程中:
commons-fileupload-1.2.1.jar
commons-io-1.3.2.jar
commons-logging-1.0.4.jar
commons-logging-api-1.1.jar
freemarker-2.3.13.jar
ognl-2.6.11.jar
struts2-core-2.1.6.jar
xwork-2.1.2.jar
如果你使用IDEA來創建項目,只要選擇Struts的2.1.6版本的支持,IDEA會自動幫你引入所需要的庫文件。
為App Engine定制你的Struts
GAE裡面的Servlet環境有一定的限制,不能使用線程(Thread),不能使用文件,還有別的一些要注意的地方。
首先,要加一個ServletContextListener的實現,在context初始化的時候,調用OgnlRuntime.setSecurityManager(null),讓Struts可以在GAE環境裡面正常跑起來:
package your.servlet.pkg;
import ognl.OgnlRuntime;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletContextEvent;
public class StrutsAppEngineAdapter implements ServletContextListener {
public void contextInitialized(ServletContextEvent servletContextEvent) {
OgnlRuntime.setSecurityManager(null);
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
在web.xml中配置這個ServletContextListener的實例:
<web-app>
<listener>
<listener-class>your.servlet.pkg.StrutsAppEngineAdapter</listener-class>
</listener>
</web-app>
做完上面的工作之後,就可以在你的GAE裡面使用Struts的功能了!
定制GAE的常用對象的Converter
Struts提供了Converter的機制,讓你常用的業務對象可以直接在頁面中顯示,或者接受類型為業務對象的請求數據。GAE裡面常用的一些對象,定義了對應的Converter之後,可以是業務代碼更加簡潔。下面介紹兩個常用對象的Converter的代碼:
TextConverter, 是針對com.google.appengine.api.datastore.Text對象的轉換器,Text是GAE的存儲中,大文本內容的保存對象,在業務中很常用。先來看看TextConverter大的代碼:
package your.servlet.pkg;
import com.google.appengine.api.datastore.Text;
import ognl.DefaultTypeConverter;
import java.util.Map;
public class TextConverter extends DefaultTypeConverter {
@Override
public Object convertValue(Map map, Object o, Class toType) {
if (toType == Text.class) {
String value = ((String[]) o)[0];
return new Text(value);
} else if (toType == String.class) {
Text text = (Text) o;
return text.getValue();
}
return null;
}
}
TextConverter的作用主要是用於在WEB頁面中,直接顯示存儲對象中的大文本內容。
另外一個是KeyConverter,是對com.google.appengine.api.datastore.Key對象的轉換器,Key是GAE中三種主鍵(Long,String,Key)的一種,在接收請求數據以及頁面顯示的時候,會經常用到。直接看代碼:
package your.servlet.pkg;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import ognl.DefaultTypeConverter;
import java.util.Map;
public class KeyConverter extends DefaultTypeConverter {
@Override
public Object convertValue(Map map, Object o, Class toType) {
if (toType == Key.class) {
String s = ((String[]) o)[0];
return KeyFactory.stringToKey(s);
} else if (toType == String.class) {
Key k = (Key) o;
return KeyFactory.keyToString(k);
}
return null;
}
}
在源代碼的根目錄,創建xwork-conversion.properties文件,內容如下:
com.google.appengine.api.datastore.Text=your.servlet.pkg.TextConverter
com.google.appengine.api.datastore.Key=your.servlet.pkg.KeyConverter
上面的工作都完成以後,就可以在你的Action裡面直接使用Key類型的類變量,無需手工去做String和Key之間的轉換!
Convention Plugin不能使用
Struts2裡面,最常用的Plugin應該是Convertion了,"零配置"即減少了寫配置文件的麻煩,代碼的組織結構看起來也清晰很多。
但是在GAE裡面,無法讀取文件系統,目前的Convertion版本還無法正常工作,非常可惜。不過建議你的GAE應用中,Action和JSP文件,還是按照Convertion Plugin的組織方式來存放,一方面有利於後面的升級和遷移工作,另一方面也讓你的應用的文件看起來更清晰。