Server basics

Let’s assume we have the following Thrift IDL:

namespace java com.example.thrift

service HelloService {
    string hello(1:string name)
}

The Apache Thrift compiler will produce some Java code under the com.example.thrift package. The most noteworthy one is HelloService.java which defines the service interfaces we will implement:

import org.apache.thrift.TException;
import org.apache.thrift.async.AsyncMethodCallback;

public class HelloService {
    public interface Iface {
        public String hello(String name) throws TException;
    }

    public interface AsyncIface {
        public void hello(String name, AsyncMethodCallback resultHandler) throws TException;
    }
    ...
}

If you are interested in going fully asynchronous, it is recommended to implement the AsyncIface interface, while it is slightly easier to implement the synchronous Iface interface:

import org.apache.thrift.TException;
import org.apache.thrift.async.AsyncMethodCallback;

public class MyHelloService implements HelloService.AsyncIface {
    @Override
    public void hello(String name, AsyncMethodCallback resultHandler) throws TException {
        resultHandler.onComplete("Hello, " + name + '!');
    }
}

// or synchronously:
public class MyHelloService implements HelloService.Iface {
    @Override
    public String hello(String name) throws TException {
        return "Hello, " + name + '!';
    }
}

Bootstraping an Armeria server

You can configure an Armeria server using the fluent builder pattern, as shown below:

import com.linecorp.armeria.common.http.HttpSessionProtocols;
import com.linecorp.armeria.common.thrift.ThriftSerializationFormats;
import com.linecorp.armeria.server.Server;
import com.linecorp.armeria.server.ServerBuilder;
import com.linecorp.armeria.server.thrift.THttpService;

HelloService.AsyncIface helloHandler = new MyHelloService();

ServerBuilder sb = new ServerBuilder();
sb.port(8080, HttpSessionProtocols.HTTP); // or just port(8080, "http")
sb.serviceAt(
        "/hello",
        THttpService.of(helloHandler, ThriftSerializationFormats.BINARY)
                    .decorate(LoggingService::new)).build();

Server server = sb.build();
server.start().join();

In the example above, we created a new ServerBuilder and added a new THttpService to it. The THttpService is bound at the path /hello and will use the TBinary format.

We also decorated the THttpService using LoggingService, which logs all Thrift calls and replies. You might be interested in decorating a service using other decorators, to gather metrics for example.

Note that you can add more than one THttpService (or any Service implementation) to a Server.

Adding a documentation service

As usual, we could browse a Thrift IDL in a text editor to see the list of the available structs and services. However, most of us will admit that it will be much nicer if we could browse such information like we do for RESTful services via Swagger.

Armeria provides a service called DocService, which discovers all THttpService in your Armeria server and lets you browse the available service operations and structs:

import com.linecorp.armeria.server.ServerBuilder;
import com.linecorp.armeria.server.docs.DocService;
import com.linecorp.armeria.server.thrift.THttpService;

ServerBuilder sb = new ServerBuilder();
sb.serviceAt("/foo/", THttpService.of(...))
  .serviceAt("/bar/", THttpService.of(...))
  .serviceUnder("/docs/", new DocService());

Note that we used serviceUnder() for DocService unlike the other services. serviceUnder() binds a service to a directory recursively (prefix match) while serviceAt() binds to a specific path only (exact-match.)

If you open http://127.0.0.1:8080/docs/ in your browser, you will see the documentation pages produced by the DocService. Here’s a sample, generated from the Cassandra Thrift IDL:

_images/docservice.png

Next steps