在泽西岛动态(取消)部署资源

2022-09-02 02:06:09

我需要在 Jersey ServletContainer 中热部署和取消部署资源。

似乎没有办法在 ResourceConfig 上“注销”资源,所以我遵循的路线是用一个新资源集替换所有资源。

尽管文档说 ResourceConfig 上的 registerResources 会替换所有资源,但浏览源代码似乎与此相矛盾。

我发现的解决方案是使用全新的 ResourceConfig 重新加载 ServletContainer。

Set<Class<?>> classes = ...
ResourceConfig config = new ResourceConfig(classes);
container.reload(config);

这工作正常,直到我部署一个导致模型验证异常的资源。在那之后,我无法让ServletContainer恢复到正确的状态。

如果我看一下源代码:

public void reload(final ResourceConfig configuration) {
    try {
        containerListener.onShutdown(this);

        webComponent = new WebComponent(webComponent.webConfig, configuration);
        containerListener = webComponent.appHandler;
        containerListener.onReload(this);
        containerListener.onStartup(this);
    } catch (final ServletException ex) {
        LOGGER.log(Level.SEVERE, "Reload failed", ex);
    }
}

ModelValidationException 是从 WebComponent 构造函数引发的。之后,任何对重新加载的调用都会导致 onShutdown 方法出现异常,这是由 ServiceLocatorImpl 的 preDestroy 方法中的 checkState 引起的。

我可以通过忽略验证错误来避免异常

ResourceConfig config = new ResourceConfig(classes);
config.property(ServerProperties.RESOURCE_VALIDATION_IGNORE_ERRORS,
    Boolean.TRUE);
container.reload(config);

然而,现在没有办法找出是否有任何错误,而是探索日志记录,这真的同样糟糕。

根据heenenee的评论,我尝试了对ServletContainer进行子类化,但是像这样的东西会带来问题,因为ResourceConfig不能放在两个WebComponent中。

我尝试在关闭之前创建WebComponent,以获得早期退出,但是如果资源中没有错误,则实际重新加载失败(因为在创建Web组件后无法修改资源配置)

@Override
public void reload(ResourceConfig configuration) {
    try {
        new WebComponent(new WebServletConfig(this), configuration);
    } catch (ServletException e) {
        LOGGER.log(Level.SEVERE, "Reload failed", e);
        List<ResourceModelIssue> resources = Collections.emptyList();
        throw new ModelValidationException(e.getMessage(), resources);
    }
    super.reload(configuration);
}

有没有另一种方法可以热取消部署资源?有没有办法在重新加载失败后重置ServletContainer?


答案 1

我认为如果不使用支持热部署的 servlet 容器,就无法实现这一点。根据我的经验,一个好的方法是使用支持OSGi的容器。你可以看看Eclipse VirgoApache Karaf

例如,在 OSGi 环境中,您可以创建模块(称为捆绑软件),这些模块可以放到扫描的文件夹中以在运行时启用功能,也可以从文件夹中移除以禁用某些功能。这类似于插件在 Eclipse IDE 中的工作方式,在 Eclipse IDE 中,新插件的安装/卸载不一定需要重新启动。


答案 2

从技术上讲,泽西岛不是一个 servlet 容器,它是一个在 servlet 容器上运行的 REST/JaxB 框架。

大多数可嵌入的 servlet 容器,Tomcat、Jetty、Grizzly 都允许您在运行时重新部署应用程序和 servlet。但是,在代码中嵌入容器时,重新部署通常不是使用的功能。

热重新部署在生产中最为有用,允许您持续部署新版本。在 Tomcat 上,您可以在同一台服务器上部署应用程序的新旧版本,并且 tomcat 确保在应用程序上的最新版本上启动新会话,但旧版本将继续使用启动它们时使用的应用程序版本。当应用程序未使用 loger 时,它将自动取消部署。


推荐