为什么我的 ORDS servlet 没有执行,即使我已经遵循了现有的指南?

Why isn't my ORDS servlet executing, even though I've followed the existing guidelines?

我目前正在编写一个旨在过滤某些请求的 ORDS 插件。我不太想让过滤工作,所以我决定 follow Oracle's provided instructions 他们的插件 API。

我已经使用 Gradle 任务配置了大部分构建,该任务会自动:

实际上,这是我的自动对应 运行:

# Assuming the JAR is cURL'd in from somewhere...
java -jar ords.war plugin build/myPlugin.jar
java -jar ords.war configdir /home/makoto/ords-configuration

...然后我将其部署到本地 IntelliJ 实例。

这是我的 servlet 的样子。这很基本。

import oracle.dbtools.plugin.api.di.annotations.Provides;
import oracle.dbtools.plugin.api.http.annotations.Dispatches;
import oracle.dbtools.plugin.api.http.annotations.PathTemplate;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Provides
@Dispatches(@PathTemplate(("/plugin/servlet/")))
public class TestServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.getWriter().println("this worked?!");
    }
}

根据文档,我相信我应该能够在 http://localhost:8080/ords/my_schema/plugin/servlet/ 访问它,但事实似乎并非如此。相反,我收到了 404:

DispatcherNotFoundException [statusCode=404, reasons=[]]
    at oracle.dbtools.http.entrypoint.Dispatcher.choose(Dispatcher.java:87)
    at oracle.dbtools.http.entrypoint.Dispatcher.dispatch(Dispatcher.java:98)
    at oracle.dbtools.http.entrypoint.EntryPoint$FilteredServlet.service(EntryPoint.java:240)
    at oracle.dbtools.http.filters.FilterChainImpl.doFilter(FilterChainImpl.java:73)
    at oracle.dbtools.url.mapping.RequestMapperImpl.doFilter(RequestMapperImpl.java:125)
    at oracle.dbtools.url.mapping.URLMappingBase.doFilter(URLMappingBase.java:103)
    at oracle.dbtools.url.mapping.filter.URLMappingFilter.doFilter(URLMappingFilter.java:148)
    at oracle.dbtools.http.filters.HttpFilter.doFilter(HttpFilter.java:47)
    at oracle.dbtools.http.filters.FilterChainImpl.doFilter(FilterChainImpl.java:64)
    at oracle.dbtools.http.cors.CORSResponseFilter.doFilter(CORSResponseFilter.java:83)
    at oracle.dbtools.http.filters.HttpResponseFilter.doFilter(HttpResponseFilter.java:45)
    at oracle.dbtools.http.filters.FilterChainImpl.doFilter(FilterChainImpl.java:64)
    at oracle.dbtools.http.errors.ErrorPageFilter.doFilter(ErrorPageFilter.java:94)
    at oracle.dbtools.http.filters.HttpFilter.doFilter(HttpFilter.java:47)
    at oracle.dbtools.http.filters.FilterChainImpl.doFilter(FilterChainImpl.java:64)
    at oracle.dbtools.http.auth.ForceAuthFilter.doFilter(ForceAuthFilter.java:44)
    at oracle.dbtools.http.filters.HttpFilter.doFilter(HttpFilter.java:47)
    at oracle.dbtools.http.filters.FilterChainImpl.doFilter(FilterChainImpl.java:64)
    at oracle.dbtools.http.filters.Filters.filter(Filters.java:47)
    at oracle.dbtools.http.entrypoint.EntryPoint.service(EntryPoint.java:82)
    at oracle.dbtools.http.entrypoint.EntryPointServlet.service(EntryPointServlet.java:49)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
    at oracle.dbtools.rt.web.HttpEndpointBase.dispatchableServices(HttpEndpointBase.java:116)
    at oracle.dbtools.rt.web.HttpEndpointBase.service(HttpEndpointBase.java:81)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)

-- snip --

我错过了什么?我不清楚什么应该是 非常 基本 servlet - 这实际上类似于他们提供的 "Hello World!" 示例 - 只是没有正确注册。

注:

我正在寻找关于这里可能发生的事情的权威答案或见解。在黑暗中猜测和拍摄对我没有好处,因为我自己一直在修补这个问题,而且我们的修补路径很可能会重叠。


虽然我不愿意在任何问题中添加图片,但我检查了 JAR 的内容以确保其中包含特定的提供程序文件。

从这个截图来看,似乎有两个...

...它们的内容是一样的...

com.foo.bar.baz.bing.servlet.TestServlet
oracle.dbtools.plugin.api.di.AnnotationsProvider

...但是当我去提取 JAR 并检查文件时,它只包含 AnnotationsProvider 行。

oracle.dbtools.plugin.api.di.AnnotationsProvider

但是等等!越来越奇怪了!

当我安装 JAR 以提取 个人 文件时,我看到很多重复项:

...这让我相信,不知何故,旧文件正在覆盖新文件。

我已经解决了这个问题。 为我指明了正确的方向。

ORDS 希望提供者通过名为 META-INF/oracle.dbtools.plugin.api.di.providers 的文件进行注册。在这个文件中是一个 classes 的列表,由它们的完全限定名称描述,并用 @Provides 注释。任何未出现在其中的class都不会被 ORDS 拾取。

正如我的问题所强调的那样,我 运行 遇到的是 JAR 中存在的重复文件名。如果我通过 Neovim 观察它,我会在一个文件中看到我的 FQN classes,在另一个文件中看到 none。如果我通过 Nautilus/File Extractor 观察它,我只会看到带有我的 FQN classes.none 的文件。

重复文件问题原来是确凿的证据。为了使它正常工作,我必须从构建的 JAR 中删除重复项。在Gradle中,实现方法是这样的:

jar {
   duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}

现在,只有正确的 *providers 文件出现了,我的 servlet 可以在 ORDS 中被命中。

我要指出这是一个惊喜;我没有预料到 JAR 中会打包任何类型的重复文件,ORDS 文档也没有就此问题发出潜在警告。我认为这是其他开发人员注意这种情况的公平灯塔。

我在演示插件 JAR 的源代码中看到它使用 SPI 注册了自己。这就是 WAR 中的 ORDS 核心找到它的方式。 ORDS 示例文件夹中提供的 Ant 任务负责在创建 JAR 时生成必要的 SPI 文件。你提到你为此使用了 Gradle 任务,所以我猜你是自己写的。

为了验证您的 Gradle 作业是否也生成了正确的 JAR,提取 Gradle 生成的插件 JAR 文件并检查是否有包含唯一内容的 /META-INF/oracle.dbtools.plugin.api.di.providers 文件您 TestServlet 的 FQN。如果不是,那肯定不会在WAR被ORDS核心发现。

您可以通过将 PluginDemo servlet 源代码替换为您自己的 servlet 内容然后按照教程中的说明使用提供的 Ant 任务构建 JAR 来确认您的插件 servlet 源代码是否正确。如果可行,那么肯定是 Gradle 任务需要修复,而不是您的插件 servlet。但是,由于问题中缺少此信息,因此无法给出详细答案。但它至少应该将您推向正确的方向,以便确定问题。