Java 11 Cloud SDK的appengine:run相当于什么?

问题描述

tl; dr::如何以Datastore工作的方式在本地运行this project? (压缩下载链接here。)

我正在将使用App Engine和数据存储的Java 8项目迁移到Java 11。

在Java 8中,我使用基于Cloud SDK的App Engine pluginmvn appengine:run上本地运行服务器,并使用mvn appengine:deploy部署到实时服务器。

我遵循了this guide,它告诉我删除appengine-web.xml文件并改用app.yaml

要部署到实时服务器,我仍然可以使用mvn appengine:deploy,无论有没有数据存储,它都可以正常工作。

要在本地部署,我运行mvn package exec:java。这对于在没有数据存储的情况下运行基本服务器的情况很好,但是如果我添加一些example Datastore code,则会出现此错误

java.lang.IllegalArgumentException: A project ID is required for this service but Could not be determined from the builder or the environment. Please set a project ID using the builder.

我知道这是因为我没有运行本地App Engine环境。但是,如果尝试使用mvn appengine:run运行本地服务器,则会收到此错误,抱怨缺少appengine-web.xml文件

[ERROR] Failed to execute goal com.google.cloud.tools:appengine-maven-plugin:2.2.0:run (default-cli) on project datastore-hello-world:
Failed to run devappserver: java.nio.file.NoSuchFileException:
C:\Users\kevin\Documents\GitHub\HappyCoding\examples\google-cloud\google-cloud-example-projects\datastore-hello-world\target\datastore-hello-world-1\WEB-INF\appengine-web.xml -> [Help 1]

因此,我陷入了需要运行需要mvn appengine:run的{​​{1}}和需要更新到Java 11的困境之间,Java 11表示要使用appengine-web.xml而不是app.yaml

Java 11 App Engine docs只说本地运行:

要在部署之前测试应用程序的功能,请使用通常使用的开发工具在本地环境中运行应用程序。

上面的GitHubzip链接包含整个项目(总共5个文件),但是为了将我的代码直接包含在问题中,以下是最重要的文件

pom.xml

appengine-web.xml

ServerMain.java

<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>datastore-hello-world</artifactId>
  <version>1</version>

  <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>
    <jetty.version>9.4.31.v20200723</jetty.version>

    <!-- Project-specific properties -->
    <mainClass>io.happycoding.ServerMain</mainClass>
    <googleCloudProjectId>YOUR_PROJECT_ID_HERE</googleCloudProjectId>
  </properties>

  <dependencies>
    <!-- Java Servlets API -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
    </dependency>

    <!-- Jetty -->
    <dependency>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-server</artifactId>
      <version>${jetty.version}</version>
    </dependency>
    <dependency>
      <groupId>org.eclipse.jetty</groupId>
      <artifactId>jetty-annotations</artifactId>
      <version>${jetty.version}</version>
    </dependency>

    <!-- Datastore -->
    <dependency>
      <groupId>com.google.cloud</groupId>
      <artifactId>google-cloud-datastore</artifactId>
      <version>1.104.0</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <!-- copy static resources like html files into the output jar file. -->
      <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>./src/main/webapp</directory></resource>
              </resources>
            </configuration>
          </execution>
        </executions>
      </plugin>

      <!-- Package everything into a single executable jar file. -->
      <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>
              <createDependencyReducedPom>false</createDependencyReducedPom>
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                  <mainClass>${mainClass}</mainClass>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>

      <!-- Exec plugin for deploying the local server. -->
      <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>${mainClass}</mainClass>
        </configuration>
      </plugin>

      <!-- App Engine plugin for deploying to the live site. -->
      <plugin>
        <groupId>com.google.cloud.tools</groupId>
        <artifactId>appengine-maven-plugin</artifactId>
        <version>2.2.0</version>
        <configuration>
          <projectId>${googleCloudProjectId}</projectId>
          <version>1</version>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

HelloWorldServlet.java

package io.happycoding;

import java.net.URL;
import org.eclipse.jetty.annotations.AnnotationConfiguration;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.webapp.WebInfConfiguration;

/**
 * Starts up the server,including a DefaultServlet that handles static files,* and any servlet classes annotated with the @WebServlet annotation.
 */
public class ServerMain {

  public static void main(String[] args) throws Exception {

    // Create a server that listens on port 8080.
    Server server = new Server(8080);
    WebAppContext webAppContext = new WebAppContext();
    server.setHandler(webAppContext);

    // Load static content from inside the jar file.
    URL webAppDir =
        ServerMain.class.getClassLoader().getResource("meta-inf/resources");
    webAppContext.setResourceBase(webAppDir.toURI().toString());

    // Enable annotations so the server sees classes annotated with @WebServlet.
    webAppContext.setConfigurations(new Configuration[]{ 
      new AnnotationConfiguration(),new WebInfConfiguration(),});

    // Look for annotations in the classes directory (dev server) and in the
    // jar file (live server)
    webAppContext.setAttribute(
        "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern",".*/target/classes/|.*\\.jar");

    // Handle static resources,e.g. html files.
    webAppContext.addServlet(DefaultServlet.class,"/");

    // Start the server! ?
    server.start();
    System.out.println("Server started!");

    // Keep the main thread alive while the server is running.
    server.join();
  }
}

如何在本地运行Java 11 App Engine服务器,以便数据存储正常工作?

解决方法

根据guillaume blaquiere在其comment中的建议,我尝试按照this guide在本地手动运行数据存储。

我在一个命令行中运行了gcloud beta emulators datastore start,看起来运行得很好,然后又在另一命令行中运行了$(gcloud beta emulators datastore env-init),但出现了这个错误:

ERROR: (gcloud.beta.emulators.datastore.env-init)
Unable to find env.yaml in the data_dir 
[/tmp/tmp.zolNZC8fnr/emulators/datastore]. 
Please ensure you have started the appropriate emulator.

我回到了运行数据存储区的第一个命令行,并且在输出中注意到了这一行:

Storage: /tmp/tmp.sxkNeaNHxo/emulators/datastore/WEB-INF/appengine-generated/local_db.bin

我将该目录路径复制到了data-dir参数中:

$(gcloud beta emulators datastore env-init --data-dir=/tmp/tmp.sxkNeaNHxo/emulators/datastore)

该命令似乎成功了(没有输出),所以我运行了本地服务器:mvn package exec:java

最后,这似乎在本地有效。

总结:

步骤1:运行此命令:gcloud beta emulators datastore start

步骤2:在输出中,找到如下所示的行:

Storage: /tmp/tmp.sxkNeaNHxo/emulators/datastore/WEB-INF/appengine-generated/local_db.bin

复制/tmp/tmp.YOUR_PATH_HERE/emulators/datastore路径。

步骤3:在另一个命令行中,运行以下命令,并粘贴到您刚复制的路径中:

$(gcloud beta emulators datastore env-init --data-dir=/tmp/tmp.YOUR_PATH_HERE/emulators/datastore)

步骤4:在第二个命令行中,运行本地服务器:mvn package exec:java

这让人感到很费解(并且没有文档记录),因此我仍然对如何简化此建议持开放态度。但是到目前为止,这至少有效。