I'm presently writing an ORDS plugin which is intended to filter certain requests. I'm not quite about to get the filtering working, so I decided to follow Oracle's provided instructions for their Plugin API.
I've configured much of the build with a Gradle task which automatically:
Effectively, this is the automated equivalent to me running:
# 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
...and I deploy this to my local IntelliJ instance.
Here is what my servlet looks like. It's pretty basic.
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?!");
}
}
I'm led to believe by the documentation that I should be able to access it at http://localhost:8080/ords/my_schema/plugin/servlet/, but that doesn't seem to be the case. I'm instead greeted with a 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 --
What am I missing? I'm unclear as to what should be a very basic servlet - which is virtually analogous to the "Hello World!" example they have provided - is simply not registering appropriately.
Note:
@Dispatches
path doesn't seem to have an effect; if it's removed or if it's present the issue remains.I am looking for authoritative answers or insights as to what could be going on here. Guesses and shots in the dark do me no good, as I've been tinkering with this myself, and there's a very good chance that our tinkering paths would have overlapped.
As loath as I am to add pictures to any question, BalusC suggested that I inspect the contents of the JAR to ensure that there's a specific providers file contained within.
From this screenshot, there appears to be two...
...and their contents are the same...
com.foo.bar.baz.bing.servlet.TestServlet
oracle.dbtools.plugin.api.di.AnnotationsProvider
...but when I go to extract the JAR and inspect the file, it only contains the AnnotationsProvider
line.
oracle.dbtools.plugin.api.di.AnnotationsProvider
But wait! It gets weirder!
When I mount the JAR to extract individual files, I see lots of duplicates:
...which leads me to believe that, somehow, the older file is overwriting the newer file.
I've figured the issue out. BalusC's suggestion pointed me in the right direction.
ORDS expects providers to be registered through a file called META-INF/oracle.dbtools.plugin.api.di.providers
. In this file is a list of classes described by their fully-qualified name which have been annotated with @Provides
. Any class which doesn't appear in there will not be picked up by ORDS.
What I was running into, as highlighted by my question, was duplicate file names present within the JAR. If I observed it through Neovim, I'd see my FQN classes in one file, and none in another. If I observed it through Nautilus/File Extractor, I'd only see the file with none of my FQN classes.
The duplicate file issue turned out to be the smoking gun. In order for me to get this to work, I had to remove duplicates from my built JAR. In Gradle, the way to accomplish this is as thus:
jar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
Now, only the correct *providers
file shows up, and my servlets are able to be hit within ORDS.
I will point out that this was a surprise; I didn't anticipate any kind of duplicate files to be packaged within the JAR, nor did ORDS documentation potentially warn about this issue. I see this as a fair beacon to other devs to be mindful of this happening.
I see in source code of the demo plugin JAR that it registers itself with a SPI. That's the way how the ORDS core in WAR finds it. The provided Ant task in ORDS example folder takes care of generating the necessary SPI files while creating the JAR. You mentioned that you used a Gradle task for this, so I gather that you wrote it yourself.
In order to verify if your Gradle job generated the correct JAR too, extract the Gradle-produced plugin JAR file and inspect if there's a /META-INF/oracle.dbtools.plugin.api.di.providers
file with the sole content the FQN of your TestServlet
. If not, then it definitely won't be discovered by ORDS core in WAR.
You could confirm whether your plugin servlet's source code is correct by replacing the PluginDemo
servlet source code with your own servlet contents and then building the JAR using the provided Ant task as instructed in the tutorial. If that works, then it's definitely the Gradle task which needs to be fixed and not your plugin servlet. However, a detailed answer on that can't be given as this information is missing in the question. But it should at least push you in the right direction in order to nail down the issue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With