Tutorial: how to create an ArcadeDB Server Plugin and custom Http handlers #711
vic0824
started this conversation in
Show and tell
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
ArcadeDB uses a server plug-in mechanism to allow adding features to the core server implementation.
In facts, the Redis, Postgres, Mongo and Gremlin drivers are all implemented as server plug-ins.
It is possible to create a custom server plug-in and register it with ArcadeDB so that it is automatically started at server start-up.
This is a very useful feature, especially in a scenario in which ArcadeDB is embedded into an application which needs to respond to http requests.
In that case, instead of starting a dedicated http server, you can exploit the Undertow http server used by ArcadeDB and extend the default http handlers provided by ArcadeDB.
To implement a custom plug-in, the following steps must be performed:
A detailed description of each follows.
Step 1 - create one or more handlers
if your handler needs an instance of a database that is passed as a query parameter in the http request, it needs to extend DatabaseAbstractHandler.
DatabaseAbstractHandler provide a Database instance as one of the parameters of the execute method, which you need to override.
This works only if your REST endpoint has the following format: /myapi/mydb/{my_par1}/{my_par2}, because DatabaseAbstractHandler looks for a query parameter that contains the database name.
If your handler does not need an instance of a database to be passed as a query parameter (because you have only one database of which you know the name or because you delegate all your queries to a dedicated class that knows how to query the appropriate database) you can extend AbstractHandler.
This is an example of a custom handler that exends AbstractHandler and that handles a GET request:
By default, requests need to be authenticated, but if you don't need it, you can add setRequireAuthentication(false) to the constructor.
Step 2 - create a ServerPlugin class
Your class implements the ServerPlugin interface, so it needs to implement all its methods, but the only method you actually need to implement is registerAPI, for the other 3 you can provide an empty implementation (they all return void).
In the registerAPI, you have access to two parameters:
This is the registerAPI method that registers the example handler described in Step 1, together with another POST handler:
When the ArcadeDB server starts, it reads the arcadedb.server.plugins property and for each plugin it executes the following (simplified) actions:
Step 3 - add the new server plugin to the arcadedb.server.plugins property
This can be done by setting the property on the command line:
-Darcadedb.server.plugins=MyPluginName:com.yourpackage.MyPlugin
or via the Java API:
GlobalConfiguration.SERVER_PLUGINS.setValue("MyPluginName:com.yourpackage.MyPlugin")
Parsing the body of a POST request
To handle a POST request, your handler needs to parse the request body.
In that case, you don't need to implement your own body parser, you can exploit the parseRequestPayload method of the AbstractHandler, which produces a String with the contents of the request body (DatabaseAbstractHandler extends AbstractHandler, so the parseRequestPayload method is available also if you extend DatabaseAbstractHandler).
Please note that the Undertow HttpServerExchange .getResponseSender() method returns a response sender which automatically sets the content-length for you.
Passing your application state to the PlugIn
You cannot pass your application state directly to the PlugIn, because it is instantiated reflectively, by using
getConstructor().newInstance()which uses the default no-args constructor, so even if you create your custom constructor which accepts a reference to your application state, it won't be invoked.If you need to pass your application state to the plugin, you have to use a static reference, e.g. a static field, a static method, a singleton or an enum.
If you only need to pass a database name, you can extend DatabaseAbstractHandler and include the database name as a query parameter in your REST-style request.
Undertow IO threads vs Worker threads
Undertow uses a pool of IO threads and a pool of Worker threads.
The default configuration is:
The IO threads respond to http requests and by default they also execute the code in the execute method of an HttpHandler.
This means that if your execute method takes a long time to run, you will block one of the IO threads.
Since the IO threads are shared between the ArcadeDB core handlers and your custom handlers, there is the risk that your custom handlers will slow down ArcadeDB, depending on how many cores are available and how many demanding requests are being served.
To minimize this risk, you should identify the handlers that could potentially take a long time to run their execute method, and dispatch them to an Undertow Worker thread.
To do that, you should add the following code at the beginning of your execute method, before all the code that does the actual request handling:
This will cause your handling code to be executed in an async way (after the return statement), thus returning the IO thread to the pool instead of blocking it for the duration of the request handling.
How to serve custom static content
Another cool feature of the ArcadeDB architecture is the fact that it includes a handler that serves static content.
It is used by ArcadeDB to serve the Studio web page, but it can be used to serve custom static content as well.
When ArcadeDB is started in a mode different from production (using the arcadedb.server.mode property), it configures a special handler (an Undertow fallback handler), which handles all requests to the root path "/" that do not map to one of the core routes.
This is done using the Undertow addPrefixPath method, which matches all routes that begins with the specified prefix.
You can exploit this feature to make the ArcadeDB Http Server serve your static content.
In facts, the special handler looks for static files using:
getClass().getClassLoader().getResourceAsStream("static" + uri), which means that it will search for resources from the root of the classpath and will therefore find your resources as long as they are in a "static" folder in your jar.To do that:
In practice:
Beta Was this translation helpful? Give feedback.
All reactions