准备工作:
下载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
这个时候可以在$APPID\war\WEB-INF\classes\$APP_PACKAGE_NAME里面就可以看到生成的class文件了。
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 SelectChannelConnector@127.0.0.1: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
出错了,看来的首页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\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 个文件
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>