App Engine Java 11 无法在实时服务器上找到或加载主类

Kev*_*man 6 java google-app-engine servlets maven google-cloud-platform

tl;dr:为什么这在本地有效,但在我部署到我的实时 App Engine 项目时无效?

我正在尝试使用 Java 11 版本的 App Engine 创建一个基于 servlet 的准系统 Web 应用程序。我正在按照本指南将一些项目从 Java 8 更新到 Java 11 。我也在使用本指南本示例。我的目标是使用 Jetty 运行一个非常简单的 Web 应用程序,该应用程序在 App Engine 中提供单个静态 HTML 文件和单个 servlet 文件。

当我在本地运行时,我的网络应用程序运行良好:

mvn clean install
mvn exec:java -Dexec.args="target/app-engine-hello-world-1.war"
Run Code Online (Sandbox Code Playgroud)

当我运行这些命令时,我index.html和我的 servlet URL 都可以正常工作。

但是当我部署到我的实时站点时:

mvn package appengine:deploy
Run Code Online (Sandbox Code Playgroud)

...命令成功,但是当我导航到我的实时 URL 时,我收到了 HTML 文件和 servlet URL 的此错误:"Error: Server Error. The server encountered an error and could not complete your request. Please try again in 30 seconds."如果我查看 Cloud 控制台中的日志,我会看到此错误:

Error: Could not find or load main class io.happycoding.Main
Caused by: java.lang.ClassNotFoundException: io.happycoding.Main
Run Code Online (Sandbox Code Playgroud)

我的设置有问题,但我没有看到任何明显的错误。

这是我项目中的文件:

pom.xml

mvn clean install
mvn exec:java -Dexec.args="target/app-engine-hello-world-1.war"
Run Code Online (Sandbox Code Playgroud)

src/main/appengine/app.yaml

mvn package appengine:deploy
Run Code Online (Sandbox Code Playgroud)

src/main/java/io/happycoding/Main.java

Error: Could not find or load main class io.happycoding.Main
Caused by: java.lang.ClassNotFoundException: io.happycoding.Main
Run Code Online (Sandbox Code Playgroud)

src/main/webapp/index.html

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>io.happycoding</groupId>
  <artifactId>app-engine-hello-world</artifactId>
  <version>1</version>
  <packaging>war</packaging>

  <properties>
    <!-- App Engine currently supports Java 11 -->
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <failOnMissingWebXml>false</failOnMissingWebXml>
  </properties>

  <dependencies>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
    </dependency>

    <dependency>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-server</artifactId>
      <version>9.4.31.v20200723</version>
    </dependency>
    <dependency>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-webapp</artifactId>
      <version>9.4.31.v20200723</version>
      <type>jar</type>
    </dependency>
    <dependency>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-util</artifactId>
      <version>9.4.31.v20200723</version>
    </dependency>
    <dependency>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-annotations</artifactId>
      <version>9.4.31.v20200723</version>
      <type>jar</type>
    </dependency>

  </dependencies>

  <build>
    <plugins>

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>3.0.0</version>
        <executions>
          <execution>
            <goals>
              <goal>java</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <mainClass>io.happycoding.Main</mainClass>
        </configuration>
      </plugin>

      <plugin>
        <groupId>com.google.cloud.tools</groupId>
        <artifactId>appengine-maven-plugin</artifactId>
        <version>2.2.0</version>
        <configuration>
          <projectId>happy-coding-gcloud</projectId>
          <version>1</version>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>
Run Code Online (Sandbox Code Playgroud)

src/main/java/io/happycoding/servlets/HelloWorldServlet.java

runtime: java11
entrypoint: 'java -cp "*" io.happycoding.Main app-engine-hello-world-1.war'
Run Code Online (Sandbox Code Playgroud)

我猜我如何设置实时站点的类路径有问题,但我没有看到任何明显的错误。

packaging属性pom.xml设置为war,我得到一个.war包含以下内容的文件:

index.html
META-INF/MANIFEST.MF
META-INF/maven/io.happycoding/app-engine-hello-world/pom.properties
META-INF/maven/io.happycoding/app-engine-hello-world/pom.xml
WEB-INF/classes/io/happycoding/Main.class
WEB-INF/classes/io/happycoding/servlets/HelloWorldServlet.class
WEB-INF/classes/lib/asm-7.3.1.jar
WEB-INF/classes/lib/asm-analysis-7.3.1.jar
WEB-INF/classes/lib/asm-commons-7.3.1.jar
WEB-INF/classes/lib/asm-tree-7.3.1.jar
WEB-INF/classes/lib/javax.annotation-api-1.3.jar
WEB-INF/classes/lib/javax.servlet-api-4.0.1.jar
WEB-INF/classes/lib/jetty-annotations-9.4.31.v20200723.jar
WEB-INF/classes/lib/jetty-http-9.4.31.v20200723.jar
WEB-INF/classes/lib/jetty-io-9.4.31.v20200723.jar
WEB-INF/classes/lib/jetty-jndi-9.4.31.v20200723.jar
WEB-INF/classes/lib/jetty-plus-9.4.31.v20200723.jar
WEB-INF/classes/lib/jetty-security-9.4.31.v20200723.jar
WEB-INF/classes/lib/jetty-server-9.4.31.v20200723.jar
WEB-INF/classes/lib/jetty-servlet-9.4.31.v20200723.jar
WEB-INF/classes/lib/jetty-util-9.4.31.v20200723.jar
WEB-INF/classes/lib/jetty-webapp-9.4.31.v20200723.jar
WEB-INF/classes/lib/jetty-xml-9.4.31.v20200723.jar
Run Code Online (Sandbox Code Playgroud)

如果我将packaging属性更改pom.xmljar,那么我会得到一个.jar包含以下内容的文件:

io/happycoding/Main.class
io/happycoding/servlets/HelloWorldServlet.class
META-INF/MANIFEST.MF
META-INF/maven/io.happycoding/app-engine-hello-world/pom.properties
META-INF/maven/io.happycoding/app-engine-hello-world/pom.xml
Run Code Online (Sandbox Code Playgroud)

我在实时站点的日志中收到此错误:

Error: Unable to initialize main class io.happycoding.Main 
Caused by: java.lang.NoClassDefFoundError: org/eclipse/jetty/server/Handler 
Run Code Online (Sandbox Code Playgroud)

这感觉像是进步,但后来我的实时服务器也出现了 404 错误,所以我觉得很卡。

我需要对上述设置进行哪些更改才能使其在本地和我的实时服务器上都能正常工作?

编辑:我可以在 App Engine 调试器中看到以下文件:

应用引擎文件

我尝试将其添加到我的pom.xml文件中:

package io.happycoding;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.webapp.Configuration.ClassList;
import org.eclipse.jetty.webapp.WebAppContext;
import io.happycoding.servlets.HelloWorldServlet;

/** Simple Jetty Main that can execute a WAR file when passed as an argument. */
public class Main {

  public static void main(String[] args) throws Exception {
    if (args.length != 1) {
      System.err.println("Usage: need a relative path to the war file to execute");
      System.exit(1);
    }
    System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.StrErrLog");
    System.setProperty("org.eclipse.jetty.LEVEL", "INFO");

    Server server = new Server(8080);

    WebAppContext webapp = new WebAppContext();
    webapp.setContextPath("/");
    webapp.setWar(args[0]);
    ClassList classlist = ClassList.setServerDefault(server);

    // Enable Annotation Scanning.
    classlist.addBefore(
        "org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
        "org.eclipse.jetty.annotations.AnnotationConfiguration");

    server.setHandler(webapp);
    server.join();
  }
}
Run Code Online (Sandbox Code Playgroud)

然后我在 App Engine 调试器中看到这些文件:

应用引擎文件

但我仍然得到同样的错误。

我相信这个问题是由于我的Main类在一个.war对类路径没有影响的文件中引起的,这就是找不到它的原因。

我如何打包我的项目以便它在本地和我的实时服务器上工作?

jcc*_*ero 4

我认为你的问题是你将Main类包含在战争本身中,而 App Engine 无法找到它。

正如您在GCP 迁移指南中看到的,该类Main是在名为 的外部依赖项中定义的simple-jetty-main

执行时,maven-dependency-plugin此依赖项将被复制到该appengine-staging目录,从而可以从 Java 类路径访问它。

这就是为什么在Main从以下位置执行命令时可以在指南中提出的示例中找到该类的原因app.yaml entrypoint

entrypoint: 'java -cp "*" com.example.appengine.demo.jettymain.Main helloworld.war'
Run Code Online (Sandbox Code Playgroud)

因此,解决方案是将您的Main类包含在另一个库中,独立于您需要部署的 war 文件。

也许您可以创建一个库(就像 Google 所做的那样simple-jetty-main),可以在您的 GCP 项目中重复使用该库来完成此任务。

只是为了测试,为了确认这一点,您可以使用库本身(您可以从https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/appengine-java11/appenginesimple-jetty-main克隆所需的代码-简单码头主)。安装它,在您的 中包含依赖项pom.xml,还包括,并按如下方式maven-dependency-plugin定义您:entrypoint

entrypoint: 'java -cp "*" com.example.appengine.demo.jettymain.Main app-engine-hello-world-1.war'
Run Code Online (Sandbox Code Playgroud)

Main对于您的评论,您将不希望将类与其余代码分开。

为了满足该要求,我们必须首先更改Main类,以便 Jetty 可以提供服务HelloWorldSevlet和静态内容。该代码实际上与您提供的代码非常相似。请原谅设置的简单性,它是基于web.xml文件的;如有必要,可以进行进一步的开发来处理注释或任何认为合适的内容:

package io.happycoding;

import java.net.URL;

import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;

public class Main {

  public static final String WEBAPP_RESOURCES_LOCATION = "META-INF/resources";

  public static void main(String[] args) throws Exception {
    System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.StrErrLog");
    System.setProperty("org.eclipse.jetty.LEVEL", "INFO");

    Server server = new Server(8080);

    URL webAppDir = Thread.currentThread().getContextClassLoader().getResource(WEBAPP_RESOURCES_LOCATION);
    if (webAppDir == null) {
      throw new RuntimeException(String.format("Unable to find %s directory into the JAR file", WEBAPP_RESOURCES_LOCATION));
    }

    WebAppContext webAppContext = new WebAppContext();
    webAppContext.setContextPath("/");
    webAppContext.setDescriptor(WEBAPP_RESOURCES_LOCATION + "/WEB-INF/web.xml");
    webAppContext.setResourceBase(webAppDir.toURI().toString());
    webAppContext.setParentLoaderPriority(true);

    server.setHandler(webAppContext);

    server.start();

    server.join();
  }
}
Run Code Online (Sandbox Code Playgroud)

静态资源可以从您选择的目录加载(它将在 中参数化pom.xml)。

例如,我创建了src/main/webapp用于存储静态内容的文件夹。

在此文件夹中,您还需要定义 - 在本例中,由于我们设置 Jetty 的方式 - 一个WEB-INF包含此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">

    <servlet>
        <servlet-name>HelloWorldServlet</servlet-name>
        <servlet-class>io.happycoding.servlets.HelloWorldServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>HelloWorldServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>

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

</web-app>
Run Code Online (Sandbox Code Playgroud)

这是tree我的源代码设置:

源代码设置

pom.xml文件与您提供的文件非常相似。我只包含maven-resources-plugin将 Web 应用程序静态内容复制到 jar 文件,以及maven-shade-plugin生成 UberJar:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>io.happycoding</groupId>
    <artifactId>app-engine-hello-world</artifactId>
    <version>1</version>
    <packaging>jar</packaging>

    <properties>
        <!-- App Engine currently supports Java 11 -->
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <failOnMissingWebXml>false</failOnMissingWebXml>
        <!-- Directory where static content resides -->
        <webapp.dir>./src/main/webapp</webapp.dir>
    </properties>

    <dependencies>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
        </dependency>

        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-server</artifactId>
            <version>9.4.31.v20200723</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-webapp</artifactId>
            <version>9.4.31.v20200723</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-util</artifactId>
            <version>9.4.31.v20200723</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-annotations</artifactId>
            <version>9.4.31.v20200723</version>
            <type>jar</type>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>3.0.0</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>java</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <mainClass>io.happycoding.Main</mainClass>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.7</version>
                <executions>
                    <execution>
                        <id>copy-web-resources</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>copy-resources</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/classes/META-INF/resources</outputDirectory>
                            <resources>
                                <resource>
                                    <directory>${webapp.dir}</directory>
                                </resource>
                            </resources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.4</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>io.happycoding.Main</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <plugin>
              <groupId>com.google.cloud.tools</groupId>
              <artifactId>appengine-maven-plugin</artifactId>
              <version>2.2.0</version>
              <configuration>
                <projectId>happy-coding-gcloud</projectId>
                <version>1</version>
              </configuration>
            </plugin>
        </plugins>
    </build>


</project>
Run Code Online (Sandbox Code Playgroud)

通过此设置,您可以通过执行以下命令在本地运行应用程序:

mvn exec:java
Run Code Online (Sandbox Code Playgroud)

您还可以直接从 java 工具在本地运行该程序:

java -jar appengine-deploy-sample-1.jar
Run Code Online (Sandbox Code Playgroud)

抱歉,我无法在 GCP 中测试设置,但我认为,根据迁移指南,您可以尝试部署应用程序,而无需entrypointapp.yaml.

如果它不起作用,您可以尝试通过配置entrypoint类似于以下内容来运行该应用程序:

entrypoint: 'java -jar appengine-deploy-sample-1.jar'
Run Code Online (Sandbox Code Playgroud)

或者可能:

entrypoint: 'java -cp "*" -jar appengine-deploy-sample-1.jar'
Run Code Online (Sandbox Code Playgroud)