HTTP Server
This page describes how to extend Stardog’s HTTP Server.
Page Contents
Registering the Service
Your extension to the server should extend com.stardog.http.server.undertow.HttpService
. You’ll need a service definition in META-INF/services
called com.stardog.http.server.undertow.HttpService
, which contains the fully-qualified class name(s) of your extension(s). That service definition should be included in your jar file and dropped into the classpath. On (re)start, Stardog will find the service and auto load it into the server.
How to
An HttpService
uses a subset of the JAX-RS specification for route definition. All HttpService
implementations are scanned when the server starts and the routes are extracted, compiled into lambdas to avoid the overhead of java.lang.reflect
and passed into the server for route handling.
For example, if you wanted to define a route that will accept only POST
requests whose body is JSON and produces binary output, it would look like this:
@POST
@Path("/path/to/my/service")
@Consumes("application/json")
@Produces("application/octet-stream")
public void myMethod(final HttpServerExchange theExchange) {
// implementation goes here
}
There’s a couple things to note here. First, the path of the route is partially defined by the @Path
annotation. That value is post-fixed to any root @Path
specified on the service itself. That complete path is normally with respect to the root of the server. However, services can have sub-services, in which case it would be relative to the parent service, or you can simply override this altogether by overriding the routes
method. Similarly, you can override the routes
method if you would like to define child services for the route. Here’s how the transaction service mounts the SPARQL protocol and SPARQL Update protocol as child services:
public Iterable<Route> routes(@Nonnull final HttpPath thePath) {
List<HttpService> aServices = Lists.newArrayList(
new SPARQLProtocol(mKernel),
new SPARQLUpdate(mKernel)
);
final HttpPath aRoot = thePath.var(VAR_TX);
return () -> Iterators.concat(aServices.stream()
.flatMap(aService -> Streams.stream(aService.routes(aRoot)))
.iterator(),
HttpService.super.routes(thePath).iterator());
}
Further, note that the route method takes a single parameter: HttpServerExchange
. This is the raw Undertow HttpServerExchange
. No attempt is made to parse out arguments such as javax.ws.rs.QueryParam
, that is left to the implementor. The only part of the exchange that is parsed is any path variables. If your path is /myservice/{db}/myaction
then db
is a variable and will match whatever is included in that segment of the path. This is included in HttpServerExchange#getQueryParameters
.
An HttpService
should either have a no-argument constructor, or a constructor that accepts a single argument of type HttpServiceLoader.ServerContext
. The ServerContext
argument will contain information about the server: initialization options such as port, or whether security is disabled, as well as a reference to Stardog itself in the event your service needs a handle to Stardog. If your service is extending Stardog in some way, for convenience, you can extend from KernelHttpService
.
The implementation of the route itself should conform to Undertow’s Guide.
Here’s the implementation of user list
:
@GET
@Produces("application/json")
public void listUsers(final HttpServerExchange theExchange) {
JsonArray aUsers = new JsonArray();
mKernel.get().getUserManager().getAllUsers().stream()
.map(JsonPrimitive::new)
.forEach(aUsers::add);
JsonObject aObj = new JsonObject();
aObj.add(HTTPAdminProtocolConsts.COLLECTION_USERS, aUsers);
final String aJSON = aObj.toString();
HttpServerExchanges.Responses.contentType(theExchange, "application/json");
HttpServerExchanges.Responses.contentLength(theExchange, aJSON.length());
theExchange.getResponseSender().send(aJSON);
}