我目前正致力于将一个 Java 8 App Engine 项目迁移到 Java 11,正如在 Google 的迁移指南中 所记录的那样。许多示例和文档都集中在 Maven 上,我已经将我能做的调整到 Gradle 并完成了大部分工作。但是,我面临着让部署的代码找到项目打包的嵌入式 Jetty 服务器的主类的问题。
我可以通过执行以下命令的内联“运行”工具在 Visual Studio 代码中本地运行项目:
C:\\Program\ Files\\Eclipse\ Adoptium\\jdk-11.0.18.10-hotspot\\bin\\java.exe -agentlib:jdwp=transport=dt_socket,server=n,suspend=y,address=localhost:56132 @C:\\Users\\test\\AppData\\Local\\Temp\\cp_dz6hms01lt5nflua2xbcqzqr4.argfile com.example.server.ServerMain
这与谷歌的 repackaging guides 之一以及执行战争时记录的内容相符。上面命令中引用的 argfile 似乎只列出了项目中使用的所有库和依赖项。
java -cp "*" com.example.appengine.demo.jettymain.Main helloworld.war
所有端点都按预期工作,我什至能够毫无问题地将代码部署到 App Engine。
非常类似于这个问题一旦部署了代码,它就无法使用指定的入口点加载主类。我意识到这听起来像是一个重复的问题,但从 Maven 到 Gradle 的感觉不同足以引起一些严重的头痛。
App.yaml
runtime: java11
entrypoint: 'java -cp "*" com.example.server.ServerMain server.war'
查看上述问题的答案,建议的解决方案是修改 maven 构建脚本以将项目打包到 jar 中,并修改入口点以像启动任何其他 jar 一样启动 Web 服务器。但是,使用 App Engine Gradle 插件时,不会生成 jar,而是执行 war 任务。此外,当我手动去本地运行 shadowJar 时,永远无法访问网络服务器进行测试。所以我相信这个解决方案可能不适用于基于 Gradle 的项目。
我试图强制战争在 build.gradle 中包含所需的清单信息,但没有成功。生成了预期的清单,但由于这是一场战争,它似乎仍然不想执行。
war {
webInf { from 'src/main/appengine'}
manifest {
attributes(
'Main-Class': 'com.example.server.ServerMain'
)
}
}
清单.MF
Manifest-Version: 1.0
Main-Class: com.example.server.ServerMain
我觉得“java -cp”入口点的一些变体应该可以工作,因为我能够在本地运行类似的东西,但我对 java 标志没有太多经验,也不确定从这里去哪里。
附加信息
我会使用 appengine-simple-jetty-main 外部 Jetty 库,如here 所用。但是,我正在运行一个嵌入式码头服务器,因为我有一个我认为不会正确应用的自定义资源配置。
ServerMain.java
import java.net.URI;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.eclipse.jetty.server.Server;
import org.glassfish.jersey.jetty.JettyHttpContainerFactory;
import org.glassfish.jersey.server.ResourceConfig;
public class ServerMain {
public static void main(String[] args) throws Exception {
try {
ResourceConfig config = new App();
Server server = JettyHttpContainerFactory.createServer(URI.create("http://0.0.0.0:8080/"), config);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
System.out.println("Shutting down the application...");
server.stop();
System.out.println("Done, exit.");
} catch (Exception e) {
Logger.getLogger(ServerMain.class.getName()).log(Level.SEVERE, null, e);
}
}));
Thread.currentThread().join();
} catch (InterruptedException ex) {
Logger.getLogger(ServerMain.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
App.java
@ApplicationPath("/*")
public class App extends ResourceConfig {
private static final Logger log = Logger.getLogger(App.class.getName());
public App() {
packages("com.example.server.handlers", "com.example.server.filters", "com.example.server.interceptors");
register(GsonJerseyProvider.class);
register(MultiPartFeature.class);
Sentry.init(options -> {
options.setDsn(System.getenv("SENTRY_URL"));
});
try {
FirebaseOptions options = new FirebaseOptions.Builder()
.setCredentials(GoogleCredentials.getApplicationDefault())
.setDatabaseUrl(System.getenv("FIREBASE_URL"))
.build();
FirebaseApp.initializeApp(options);
} catch (IllegalStateException e) {
log.severe(e.getMessage());
Sentry.captureException(e);
} catch (IOException e) {
log.severe(e.getMessage());
Sentry.captureException(e);
}
}
}
所以我的问题是,如何将 Gradle 项目打包到一个 war/jar 中,既可以在本地执行又可以通过“appengineDeploy”任务部署?