準備工作:
下載apache-ant, googleappengine-sdk, jdk等工具
寫一個簡單的環境設定指令碼
setenv.bat
@echo offcd %~dp0 && echo CWD=%cd%set GAE_HOME=""for /d %%i in (appengine*) do (set GAE_HOME=%CD%\%%i)echo GAE_HOME=%GAE_HOME%set JAVA_HOME=""for /d %%i in (jdk*) do (set JAVA_HOME=%CD%\%%i)echo JAVA_HOME=%JAVA_HOME%set ANT_HOME=""for /d %%i in (apache-ant-*) do (set ANT_HOME=%CD%\%%i)echo ANT_HOME=%ANT_HOME%SET PATH=%JAVA_HOME%\BIN;%GAE_HOME%\BIN;%ANT_HOME%\BIN;%PATH%;@echo on%comspec%
關於jsp編譯失敗的問題,可以參考這個,
http://stackoverflow.com/questions/5622726/unable-to-update-app-failed-to-compile-jsp-files
所以要修改上面的指令碼,保證JAVA_HOME的bin路徑是在本地PATH路徑之前,因為path裡面很可能包含了jre的java.exe的路徑.
1, 複製庫檔案到APPID目錄下的src子目錄
cp -rf $GAE_SDK_HOME/{shared, user} src
按照sdk目錄的lib字母下的readme檔案的說明,shared和user是和使用者app的執行與編譯相關的。
2, 掃描所有的jar包
在你的app項目的src目錄下執行
for /r %i in (*.jar) do @echo %i
得到:
D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\user\orm\jdo2-api-2.3-eb.jar D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\shared\appengine-local-runtime-shared.jar D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\shared\el-api.jar D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\shared\jsp-api.jar D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\shared\servlet-api.jar D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\shared\jsp\repackaged-appengine-ant-1.7.1.jar D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\shared\jsp\repackaged-appengine-ant-launcher-1.7.1.jar D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\shared\jsp\repackaged-appengine-jasper-6.0.29.jar D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\shared\jsp\repackaged-appengine-jasper-el-6.0.29.jar D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\shared\jsp\repackaged-appengine-tomcat-juli-6.0.29.jar D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\user\appengine-api-1.0-sdk-1.7.7.jar D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\user\appengine-api-labs-1.7.7.jar D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\user\appengine-jsr107cache-1.7.7.jar D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\user\jsr107cache-1.1.jar D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\user\orm\datanucleus-appengine-1.0.10.final.jar D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\user\orm\datanucleus-core-1.1.5.jar D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\user\orm\datanucleus-jpa-1.1.5.jar D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\user\orm\geronimo-jpa_3.0_spec-1.1.1.jar D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\user\orm\geronimo-jta_1.1_spec-1.1.1.jar D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\user\orm\jdo2-api-2.3-eb.jar
編寫2個簡單的java類
Res.java
package bagebit;import com.google.appengine.api.datastore.DatastoreService;import com.google.appengine.api.datastore.DatastoreServiceFactory;import com.google.appengine.api.datastore.Entity;import com.google.appengine.api.datastore.Key;import com.google.appengine.api.datastore.KeyFactory;import java.io.IOException;import java.util.logging.Logger;import javax.servlet.http.*;public class Res extends HttpServlet { private static final Logger log = Logger.getLogger(Res.class.getName()); public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {log.info("Res::doGet called at: " + (new java.util.Date()).toString()); resp.setContentType("text/html;charset=UTF-8"); resp.getWriter().println("<b>Hello, " + req.getRequestURI() +"</b>"); }}
User.java
package bagebit;import com.google.appengine.api.datastore.DatastoreService;import com.google.appengine.api.datastore.DatastoreServiceFactory;import com.google.appengine.api.datastore.Entity;import com.google.appengine.api.datastore.Key;import com.google.appengine.api.datastore.KeyFactory;import java.io.IOException;import java.util.logging.Logger;import javax.servlet.http.*;public class User extends HttpServlet { private static final Logger log = Logger.getLogger(User.class.getName()); public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {log.info("User::doGet called at: " + (new java.util.Date()).toString()); resp.setContentType("text/html;charset=UTF-8"); resp.getWriter().println("<b>Hello, " + req.getRequestURI() +"</b>"); }}
在app目錄下,執行batbuild.bat指令碼編譯java檔案:
set APPID=bagebit
set CLASSPATH="D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\user\orm\jdo2-api-2.3-eb.jar;D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\shared\appengine-local-runtime-shared.jar;D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\shared\el-api.jar;D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\shared\jsp-api.jar;D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\shared\servlet-api.jar;D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\shared\jsp\repackaged-appengine-ant-1.7.1.jar;D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\shared\jsp\repackaged-appengine-ant-launcher-1.7.1.jar;D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\shared\jsp\repackaged-appengine-jasper-6.0.29.jar;D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\shared\jsp\repackaged-appengine-jasper-el-6.0.29.jar;D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\shared\jsp\repackaged-appengine-tomcat-juli-6.0.29.jar;D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\user\appengine-api-1.0-sdk-1.7.7.jar;D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\user\appengine-api-labs-1.7.7.jar;D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\user\appengine-jsr107cache-1.7.7.jar;D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\user\jsr107cache-1.1.jar;D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\user\orm\datanucleus-appengine-1.0.10.final.jar;D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\user\orm\datanucleus-core-1.1.5.jar;D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\user\orm\datanucleus-jpa-1.1.5.jar;D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\user\orm\geronimo-jpa_3.0_spec-1.1.1.jar;D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\src\user\orm\geronimo-jta_1.1_spec-1.1.1.jar"
javac -encoding UTF-8 src\%APPID%\*.java -d war\WEB-INF\classes
這個時候可以在$APPID\war\WEB-INF\classes\$APP_PACKAGE_NAME裡面就可以看到產生的class檔案了。
User.java -> User.classRes.java -> Res.class
配置servlet映射:
開啟$APPID/war/WEB-INF/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 xmlns="http://java.sun.com/xml/ns/javaee" version="2.5"> <servlet> <servlet-name>user</servlet-name> <servlet-class>bagebit.User</servlet-class> </servlet> <servlet-mapping> <servlet-name>user</servlet-name> <url-pattern>/user</url-pattern> </servlet-mapping> <servlet> <servlet-name>res</servlet-name> <servlet-class>bagebit.Res</servlet-class> </servlet> <servlet-mapping> <servlet-name>res</servlet-name> <url-pattern>/res</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list></web-app>
運行測試用的jetty伺服器
D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit>dev_appserver -p 8888 war2013-4-22 17:30:41 com.google.apphosting.utils.config.AppEngineWebXmlReader readAppEngineWebXml資訊: Successfully processed D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\war\WEB-INF/appengine-web.xml2013-04-22 17:30:41.379:INFO::Logging to STDERR via org.mortbay.log.StdErrLog2013-4-22 17:30:41 com.google.apphosting.utils.config.AbstractConfigXmlReader readConfigXml資訊: Successfully processed D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\war\WEB-INF/web.xml2013-4-22 17:30:41 com.google.appengine.tools.development.SystemPropertiesManager setSystemProperties資訊: Overwriting system property key 'java.util.logging.config.file', value 'D:\webdev\gae\appengine-java-sdk-1.7.7\config\sdk\logging.properties' with value 'WEB-INF/logging.properties' from 'D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit\war\WEB-INF\appengine-web.xml'2013-04-22 17:30:41.695:INFO::jetty-6.1.x2013-04-22 17:30:42.671:INFO::Started SelectChannelConnector@127.0.0.1:88882013-4-22 9:30:42 com.google.appengine.tools.development.AbstractServer startup資訊: Server default is running at http://localhost:8888/2013-4-22 9:30:42 com.google.appengine.tools.development.AbstractServer startup資訊: The admin console is running at http://localhost:8888/_ah/admin2013-4-22 9:30:42 com.google.appengine.tools.development.DevAppServerImpl start資訊: Dev App Server is now running
在瀏覽器裡面測試
HTTP ERROR 500Problem accessing /. Reason: Unable to compile class for JSP: An error occurred at line: 7 in the generated java fileOnly a type can be imported. com.google.appengine.api.users.User resolves to a packageAn error occurred at line: 8 in the generated java fileOnly a type can be imported. com.google.appengine.api.users.UserService resolves to a packageAn error occurred at line: 9 in the generated java fileOnly a type can be imported. com.google.appengine.api.users.UserServiceFactory resolves to a package
出錯了,看來的首頁index.jsp編譯失敗了。
原因是沒有複製相應的jsp支援庫到WEB-INF的lib目錄下。
從剛才複製過去的src目錄下的user子目錄複製,或者從sdk的lib目錄的user子目錄複製,保持檔案夾結構。
D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebit>xcopy /Y /e /s src\user war\WEB-INF\libsrc\user\appengine-api-1.0-sdk-1.7.7.jarsrc\user\appengine-api-labs-1.7.7.jarsrc\user\appengine-jsr107cache-1.7.7.jarsrc\user\jsr107cache-1.1.jarsrc\user\orm\datanucleus-appengine-1.0.10.final.jarsrc\user\orm\datanucleus-core-1.1.5.jarsrc\user\orm\datanucleus-jpa-1.1.5.jarsrc\user\orm\geronimo-jpa_3.0_spec-1.1.1.jarsrc\user\orm\geronimo-jta_1.1_spec-1.1.1.jarsrc\user\orm\jdo2-api-2.3-eb.jar複製了 10 個檔案
shared目錄就不必了,看lib的readme文檔。
具體複製那些jar包是必要的可以參考guestbook例子裡面的WEB-INF/lib下面的樣子。
注意:在update到伺服器的時候,請記住把你的項目裡面gae內建的的jar包刪掉。
比如,剛才user目錄下複製過來的那些jar包都不要上傳了,自己額外添加的3rd的jar包可以一起上傳上去。因為sdk內建的這些jar包,伺服器已經為你準備好這些了。
否則很多巨大的jar包上傳可能會失敗,出現什麼--enable-jar-splitting的提示。
注意:gae內建的demos裡面的那些例子,也就guestbook添加了輔助的jar包,所以用dev_appserver -p 8888 war可以測試,其他的例子好像都不能運行,起碼我實驗的幾個是不行的。
OK, 頁面出來了。
改個簡單的jsp頁面
<%@ page contentType="text/html;charset=UTF-8" language="java" %><%@ page import="java.util.List" %><%@ page import="com.google.appengine.api.users.User" %><%@ page import="com.google.appengine.api.users.UserService" %><%@ page import="com.google.appengine.api.users.UserServiceFactory" %><%@ page import="com.google.appengine.api.datastore.DatastoreServiceFactory" %><%@ page import="com.google.appengine.api.datastore.DatastoreService" %><%@ page import="com.google.appengine.api.datastore.Query" %><%@ page import="com.google.appengine.api.datastore.Entity" %><%@ page import="com.google.appengine.api.datastore.FetchOptions" %><%@ page import="com.google.appengine.api.datastore.Key" %><%@ page import="com.google.appengine.api.datastore.KeyFactory" %><%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %><html> <head> <link type="text/css" rel="stylesheet" href="/stylesheets/main.css" /> </head> <body> <form action="/res?write" method="Post"> <div><input type="text" name="guestbookName" value="${fn:escapeXml(guestbookName)}"/></div> <div><input type="submit" value="Commit" /></div> </form> </body></html>
不需要重啟伺服器,jsp檔案可以立即生效的。
這是jetty的可配置選項。
提交一下到servlet,讓java來處理吧。
報錯了,HTTP405,
原因是,index.jsp是用post方式請求的,但是res.java沒有實現doPost()方法
更有用的來了,開始吧。。。
後端儲存,即“資料庫操作”, 請先複習google的資料存放區策略,google的大資料本身很厲害,有google做我們的後端,可以放心了。
https://developers.google.com/appengine/docs/java/gettingstarted/usingdatastore
建立一個Broker.java
package bagebit;import com.google.appengine.api.datastore.DatastoreService;import com.google.appengine.api.datastore.DatastoreServiceFactory;import com.google.appengine.api.datastore.Entity;import com.google.appengine.api.datastore.Key;import com.google.appengine.api.datastore.KeyFactory;import java.io.IOException;import java.util.logging.Logger;import javax.servlet.http.*;public class Broker extends HttpServlet { private static final Logger log = Logger.getLogger(Broker.class.getName()); public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {log.info("Broker::doGet called at: " + (new java.util.Date()).toString()); resp.setContentType("text/html;charset=UTF-8"); resp.getWriter().println("<b>Hello, " + req.getRequestURI() +"</b>"); }public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {log.info("Res::doPost called at: " + (new java.util.Date()).toString()); resp.setContentType("text/html;charset=UTF-8"); resp.getWriter().println("<b>Hello, " + req.getRequestURI() +"</b>"); }}
修改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 xmlns="http://java.sun.com/xml/ns/javaee" version="2.5"> <servlet> <servlet-name>SERVLET_KEY_do</servlet-name> <servlet-class>bagebit.Broker</servlet-class> </servlet> <servlet-mapping> <servlet-name>SERVLET_KEY_do</servlet-name> <url-pattern>/do</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list></web-app>