问题描述
tl; dr::如何以Datastore工作的方式在本地运行this project? (压缩下载链接here。)
我正在将使用App Engine和数据存储的Java 8项目迁移到Java 11。
在Java 8中,我使用基于Cloud SDK的App Engine plugin在mvn 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只说本地运行:
要在部署之前测试应用程序的功能,请使用通常使用的开发工具在本地环境中运行应用程序。
上面的GitHub和zip链接包含整个项目(总共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
这让人感到很费解(并且没有文档记录),因此我仍然对如何简化此建议持开放态度。但是到目前为止,这至少有效。