Skip to content

Custom Service

Paul Rogers edited this page Dec 17, 2021 · 1 revision

Druid allows many extensions. Here we look at how to create a custom service.

Command

The first step is to create a command. Create a file called META-INF/services/org.apache.druid.cli.CliCommandCreator in your resources folder. This is a standard Java ServiceLoader file. Include the name of the command you'll use to start your service:

com.whatever.extension.MyCommandCreator

Then, create that class:

package com.whatever.extension;

import io.airlift.airline.Cli.CliBuilder;
import org.apache.druid.cli.CliCommandCreator;

public class MyCommandCreator implements CliCommandCreator
{
  @Override
  public void addCommands(CliBuilder builder)
  {
    builder.withCommand(MyCommand.class);
  }
}

As a sanity check, create a "stub" command:

@Command(
    name = "example",
    description = "Launches the Example service"
)
public class MyCommand implements Runnable
{
  @Override
  @SuppressForbidden(reason = "System#out")
  public void run()
  {
    System.out.println("Welcome to my extension.");
  }
}

Test to ensure that the command works. The easiest way to do that is to have Druid itself open in your IDE, with the command part of Druid. The trick here is that you must ensure that your package on the class path. Launch a debug session as described here for a regular Druid server, but change the program option to your command, which here is example. When you run the program, you should see the following on stdout:

Welcome to my extension.

If you get an error, look at the list of loaded extensions. If yours is not present, double-check that your project appears in the class path when launching Druid. If you get a nasty stack trace, then perhaps the text file is in the wrong location, or has the wrong class name. Figure out any issues before moving on to the next step.

Server

The easiest way to create a service is to use the built-in CliCustomNodeRole class as your command. This creates a generic command and server called custom-node-role, on ports 9301 and 9501. Ok for testing, or something quick, but not quite enough for serious work.

Instead, create your own version. Replace the prior MyCommand with a copy (sorry) of the code from CliCustomNodeRole. Make the following changes:

  • Command name and description: change to whatever you want.
  • SERVICE_NAME: change to the name of your service. This is the name announced to the Druid cluster.
  • PORT, TLS_PORT: Choose a default port that does not conflict with other Druid services.
  • Add to UNSECURED_PATHS the following: "/example/hello".
  • Add the following line to the lambda in getModules() to register our JAX-WS web resource (see below).
          Jerseys.addResource(binder, MyResource.class);

JAX-RS Resource

Druid uses JAX-WS (AKA "Jersey") to configure REST resources. Here's a super simple example:

@Path("/example")
public class MyResource
{
  @GET
  @Path("/hello")
  @Produces(MediaType.TEXT_HTML)
  public String getCompactionProgress()
  {
    return "<h1>Welcome to my service.</h1>";
  }
}

Test the above by running Druid again. Now it should stay running and you should be able to navigate to http:://localhost:XXXX/example/hello, where XXXX is the port number you chose. The welcome message should appear. You can also check the health of your service: http:://localhost:XXXX/status/health which should return true.

At this point, you are ready to add your actual REST services.

Properties

Your service can accept properties. Create a directory for your service within your existing config directory. as described here. There is one file per service. Then, you can change the ports as:

These can be customized via the properties file as:

druid.serviceName=example
druid.plaintextPort=9988
druid.tlsPort=9999

For this to work, you must include this directory on the class path (in place of any of the standard services) as described here.