Link Search Menu Expand Document
Start for Free

HTTP Server

This page describes how to extend Stardog’s HTTP Server.

Page Contents
  1. Registering the Service
  2. How to

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);
}