准备工作:
下载apache-ant, googleappengine-sdk, jdk等工具
写一个简单的环境设置脚本
setenv.bat
@echo off cd %~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>"); } }
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>"); } }
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
User.java -> User.class Res.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 war 2013-4-22 17:30:41 com.google.apphosting.utils.config.AppEngineWebXmlReader read AppEngineWebXml 信息: Successfully processed D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebi t\war\WEB-INF/appengine-web.xml 2013-04-22 17:30:41.379:INFO::Logging to STDERR via org.mortbay.log.StdErrLog 2013-4-22 17:30:41 com.google.apphosting.utils.config.AbstractConfigXmlReader re adConfigXml 信息: Successfully processed D:\webdev\gae\appengine-java-sdk-1.7.7\demos\bagebi t\war\WEB-INF/web.xml 2013-4-22 17:30:41 com.google.appengine.tools.development.SystemPropertiesManage r 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\b agebit\war\WEB-INF\appengine-web.xml' 2013-04-22 17:30:41.695:INFO::jetty-6.1.x 2013-04-22 17:30:42.671:INFO::Started [email protected]:8888 2013-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/admin 2013-4-22 9:30:42 com.google.appengine.tools.development.DevAppServerImpl start 信息: Dev App Server is now running
在浏览器里面测试
HTTP ERROR 500 Problem accessing /. Reason: Unable to compile class for JSP: An error occurred at line: 7 in the generated java file Only a type can be imported. com.google.appengine.api.users.User resolves to a package An error occurred at line: 8 in the generated java file Only a type can be imported. com.google.appengine.api.users.UserService resolves to a package An error occurred at line: 9 in the generated java file Only a type can be imported. com.google.appengine.api.users.UserServiceFactory resolves to a package
原因是没有复制相应的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\lib src\user\appengine-api-1.0-sdk-1.7.7.jar src\user\appengine-api-labs-1.7.7.jar src\user\appengine-jsr107cache-1.7.7.jar src\user\jsr107cache-1.1.jar src\user\orm\datanucleus-appengine-1.0.10.final.jar src\user\orm\datanucleus-core-1.1.5.jar src\user\orm\datanucleus-jpa-1.1.5.jar src\user\orm\geronimo-jpa_3.0_spec-1.1.1.jar src\user\orm\geronimo-jta_1.1_spec-1.1.1.jar src\user\orm\jdo2-api-2.3-eb.jar 复制了 10 个文件
具体复制那些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>"); } }
<?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>