Java 中的 GAE Restful Web 服务

Posted

技术标签:

【中文标题】Java 中的 GAE Restful Web 服务【英文标题】:GAE Restful Web Service in Java 【发布时间】:2014-04-09 16:26:44 【问题描述】:

我想用 Java 为谷歌应用引擎开发 RESTful Web 服务。我使用的工具是

Eclipse Kepler Service Release 2 (Build id: 20140224-0627)
Google AppEngine Java SDK 1.9.2

我使用过许多版本的 Jersey,例如1.8、1.5 等。如果我在本地服务器上运行项目,它可以正常工作,但是当我将它部署到谷歌应用引擎上时,我可以打开欢迎网页但无法访问网络服务。

正如https://code.google.com/p/googleappengine/wiki/WillItPlayInJava 链接中提到的,GAE 支持 Jersey 1.5,但是当我在我的项目中导入这些 jars 文件时,我无法在 GAE 上部署它。

我不知道我应该使用哪个版本的泽西岛 谷歌 AppEngine Java SDK 1.9.2 还是我应该使用其他版本的 SDK。

我的 web.xml 文件

<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">

    <display-name>com.cit.cittracker</display-name>
    <servlet>
        <servlet-name>Sample Web Service</servlet-name>
        <servlet-class>
            com.sun.jersey.spi.container.servlet.ServletContainer
        </servlet-class>
        <init-param>
            <param-name>com.sun.jersey.config.property.packages</param-name>
            <param-value>com.cit.cittracker</param-value>
        </init-param>
        <init-param> 
            <param-name>com.sun.jersey.config.feature.DisableWADL</param-name> 
            <param-value>true</param-value> 
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>Sample Web Service</servlet-name>
        <url-pattern>/restservices/*</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
    </welcome-file-list>
</web-app>

我的网络服务类

package com.aakib.sample;

import javax.ws.rs.GET;
import javax.ws.rs.Path; 
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.PathParam;

@Path("/hello")
public class SampleHello 

    /*This method is called if TEXT_PLAIN is requested*/
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String sayPlainTextHello() 
        return "Hello Jersey" + "(In Text Format)";
    

    /*This method is called if XML is requested*/
    @GET  
    @Produces(MediaType.TEXT_XML)
    public String sayXMLHello() 
        return "<?xml version=\"1.0\"?>" + "<hello>Hello Jersey</hello>"
                                            + "<format>XML</format>";  
    

    /*This method is called if HTML is requested*/
    @GET
    @Produces(MediaType.TEXT_HTML)
    public String sayHtmlHello() 
        return "<html> " + "<title>" + "Hello Jersey" + "</title>"
            + "<body><h1>" + "Hello Jersey" + "</h1><h2>In HTML Format<h2></body>" + "</html> ";
    

    /*This method is called if HTML is requested and have two parameters*/
    @GET
    @Path("name")
    @Produces(MediaType.TEXT_HTML)
    public String sayHtmlHelloWith1Param(@PathParam("name") String vName) 
        return "<html> " + "<title>" + "Hello Jersey" + "</title>"
            + "<body><h1>Hello " + vName + "</h1><h2>In HTML Format</h2>" + 
                "<h3>1 Value Passed</h3>" + "</body>" + "</html> ";
    

    /*This method is called if HTML is requested and have two parameters*/
    @GET
    @Path("name/surname")
    @Produces(MediaType.TEXT_HTML)
    public String sayHtmlHelloWith2Param(@PathParam("name") String vName, @PathParam("surname") String vSurname) 
        return "<html> " + "<title>" + "Hello Jersey" + "</title>"
                + "<body><h1>Hello " + vName + " " + vSurname + "</h1><h2>In HTML Format</h2>" + 
                    "<h3>2 Value Passed</h3>" + "</body>" + "</html> ";
    

当我尝试使用 Jersey 2.5 和 Jersey 1.17 时,我在本地服务器上遇到了错误

Apr 12, 2014 2:10:45 AM java.util.prefs.WindowsPreferences <init>
WARNING: Could not open/create prefs root node Software\JavaSoft\Prefs at root 0x80000002. Windows RegCreateKeyEx(...) returned error code 5.
Apr 12, 2014 2:10:47 AM com.google.apphosting.utils.config.AppEngineWebXmlReader readAppEngineWebXml
INFO: Successfully processed B:\Programming\Java EE\CIT Tracker Web Service\war\WEB-INF/appengine-web.xml
Apr 12, 2014 2:10:47 AM com.google.apphosting.utils.config.AbstractConfigXmlReader readConfigXml
INFO: Successfully processed B:\Programming\Java EE\CIT Tracker Web Service\war\WEB-INF/web.xml
Apr 12, 2014 2:10:47 AM com.google.appengine.tools.development.SystemPropertiesManager setSystemProperties
INFO: Overwriting system property key 'java.util.logging.config.file', value 'C:\Program Files (x86)\Google AppEngine Java SDK 1.9.2\config\sdk\logging.properties' with value 'WEB-INF/logging.properties' from 'B:\Programming\Java EE\CIT Tracker Web Service\war\WEB-INF\appengine-web.xml'
Apr 12, 2014 2:10:47 AM com.google.apphosting.utils.jetty.JettyLogger info
INFO: Logging to JettyLogger(null) via com.google.apphosting.utils.jetty.JettyLogger
Apr 12, 2014 2:10:47 AM com.google.apphosting.utils.jetty.JettyLogger info
INFO: jetty-6.1.x
Apr 12, 2014 2:10:48 AM com.sun.jersey.api.core.PackagesResourceConfig init
INFO: Scanning for root resource and provider classes in the packages:
  com.cit.cittracker
Apr 12, 2014 2:10:48 AM com.google.apphosting.utils.jetty.JettyLogger warn
WARNING: failed CIT Tracker WebService: java.lang.IncompatibleClassChangeError: Implementing class
Apr 12, 2014 2:10:48 AM com.google.apphosting.utils.jetty.JettyLogger warn
WARNING: failed com.google.appengine.tools.development.DevAppEngineWebAppContext@7adb5354/,B:\Programming\Java EE\CIT Tracker Web Service\war: java.lang.IncompatibleClassChangeError: Implementing class
Apr 12, 2014 2:10:48 AM com.google.apphosting.utils.jetty.JettyLogger warn
WARNING: failed JettyContainerService$ApiProxyHandler@4c73cedb: java.lang.IncompatibleClassChangeError: Implementing class
Apr 12, 2014 2:10:48 AM com.google.apphosting.utils.jetty.JettyLogger warn
WARNING: Error starting handlers
java.lang.IncompatibleClassChangeError: Implementing class
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at java.security.SecureClassLoader.defineClass(Unknown Source)
    at java.net.URLClassLoader.defineClass(Unknown Source)
    at java.net.URLClassLoader.access$100(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at com.google.appengine.tools.development.IsolatedAppClassLoader.loadClass(IsolatedAppClassLoader.java:216)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at com.sun.jersey.api.core.ScanningResourceConfig.init(ScanningResourceConfig.java:79)
    at com.sun.jersey.api.core.PackagesResourceConfig.init(PackagesResourceConfig.java:104)
    at com.sun.jersey.api.core.PackagesResourceConfig.<init>(PackagesResourceConfig.java:78)
    at com.sun.jersey.api.core.PackagesResourceConfig.<init>(PackagesResourceConfig.java:89)
    at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:696)
    at com.sun.jersey.spi.container.servlet.WebComponent.createResourceConfig(WebComponent.java:674)
    at com.sun.jersey.spi.container.servlet.WebComponent.init(WebComponent.java:203)
    at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:374)
    at com.sun.jersey.spi.container.servlet.ServletContainer.init(ServletContainer.java:557)
    at javax.servlet.GenericServlet.init(GenericServlet.java:212)
    at org.mortbay.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:440)
    at org.mortbay.jetty.servlet.ServletHolder.doStart(ServletHolder.java:263)
    at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
    at org.mortbay.jetty.servlet.ServletHandler.initialize(ServletHandler.java:685)
    at org.mortbay.jetty.servlet.Context.startContext(Context.java:140)
    at org.mortbay.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1250)
    at org.mortbay.jetty.handler.ContextHandler.doStart(ContextHandler.java:517)
    at org.mortbay.jetty.webapp.WebAppContext.doStart(WebAppContext.java:467)
    at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
    at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:130)
    at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
    at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:130)
    at org.mortbay.jetty.Server.doStart(Server.java:224)
    at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
    at com.google.appengine.tools.development.JettyContainerService.startContainer(JettyContainerService.java:254)
    at com.google.appengine.tools.development.AbstractContainerService.startup(AbstractContainerService.java:305)
    at com.google.appengine.tools.development.AutomaticInstanceHolder.startUp(AutomaticInstanceHolder.java:26)
    at com.google.appengine.tools.development.AbstractModule.startup(AbstractModule.java:79)
    at com.google.appengine.tools.development.Modules.startup(Modules.java:97)
    at com.google.appengine.tools.development.DevAppServerImpl.doStart(DevAppServerImpl.java:255)
    at com.google.appengine.tools.development.DevAppServerImpl.access$000(DevAppServerImpl.java:47)
    at com.google.appengine.tools.development.DevAppServerImpl$1.run(DevAppServerImpl.java:213)
    at com.google.appengine.tools.development.DevAppServerImpl$1.run(DevAppServerImpl.java:211)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.google.appengine.tools.development.DevAppServerImpl.start(DevAppServerImpl.java:211)
    at com.google.appengine.tools.development.DevAppServerMain$StartAction.apply(DevAppServerMain.java:277)
    at com.google.appengine.tools.util.Parser$ParseResult.applyArgs(Parser.java:48)
    at com.google.appengine.tools.development.DevAppServerMain.run(DevAppServerMain.java:219)
    at com.google.appengine.tools.development.DevAppServerMain.main(DevAppServerMain.java:210)

Apr 12, 2014 2:10:48 AM com.google.apphosting.utils.jetty.JettyLogger info
INFO: Started SelectChannelConnector@127.0.0.1:8888
Apr 12, 2014 2:10:48 AM com.google.appengine.tools.development.AbstractModule startup
INFO: Module instance default is running at http://localhost:8888/
Apr 12, 2014 2:10:48 AM com.google.appengine.tools.development.AbstractModule startup
INFO: The admin console is running at http://localhost:8888/_ah/admin
Apr 12, 2014 2:10:48 AM com.google.appengine.tools.development.DevAppServerImpl doStart
INFO: Dev App Server is now running

当我尝试在浏览器中打开它时显示

HTTP ERROR: 404

Problem accessing /. Reason:

    NOT_FOUND
Powered by Jetty://

【问题讨论】:

当您尝试访问 Web 服务时,日志中出现什么错误? 另外,试试最新版本的 Jersey: 1.18 可能由于某些原因服务器没有预热。这可能会发生。本地服务器和生产服务器之间存在一些差异。检查日志,尤其是预热请求。然后检查您的实例是否处于活动状态。也许您已经超出了免费版本的限制。顺便说一句,泽西岛的最新版本是 2.7。但为了安全起见,请使用 2.5。最新的有一些兑现问题,如果不修复可能无法正常工作或根本无法工作:P 球衣 1.18 不工作。现在我将尝试使用 Jersey 2.5。 我尝试了 Jersey 1.17 和 2.5 但没有运气。我用错误日志编辑了我的问题 【参考方案1】:

我使用Jersey 1.18Google App Engine SDK 1.8.8 没有问题。尝试使用这些版本结束一切都会正常工作。

【讨论】:

【参考方案2】:

错误信息

java.lang.IncompatibleClassChangeError: Implementing class

应该是线索 - 虽然它没有太大帮助,但它可能意味着应用程序已在类路径的某个位置加载了一个类及其接口,但它们不兼容。

在错误日志中稍高一点:

WARNING: failed com.google.appengine.tools.development.DevAppEngineWebAppContext@7adb5354/,B:\Programming\Java EE\CIT Tracker Web Service\war: java.lang.IncompatibleClassChangeError: Implementing class

看起来它正在使用本地 Windows 驱动器上的路径,其中包含空格 - 不要在路径中使用空格,Microsoft 允许它从 Windows95 开始,此后一直出现问题,并且不指定路径部署到您的本地驱动器 - 它需要与应用程序相关。

希望这会有所帮助,抱歉没有更具体。

【讨论】:

以上是关于Java 中的 GAE Restful Web 服务的主要内容,如果未能解决你的问题,请参考以下文章

java中的Restful Web应用程序(安全性)

带有 java 模块的 GAE 通道

如何使用 Spring RESTful Web 服务处理 CSRF 保护?

使用 GAE 的 Hibernate 的访问被拒绝 WEB-INF/类

[Go] Golang练习项目-web客服系统即时通讯websocket项目go-fly

Java Web学习总结(44)—— RESTful 架构和 RESTful API 设计总结