Baratine on GitHub

REST the Easy Way: Baratine vs JAX-RS

Introduction

From the first look of things, Baratine looks very much like JAX-RS. Like JAX-RS, Baratine uses annotations like @Get and @Post to implement REST methods. However, the similarities end there. Baratine is reactive, asynchronous, provides automatic persistence, and much faster to boot.

Simplicity

Baratine Bookstore

A simple bookstore application in Baratine looks like:

Baratine Bookstore
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@Service
public class Bookstore
{
  private Map<Long,Book> books = new HashMap<Long,Book>();

  public Bookstore()
  {
    Book book = new Book();

    book.setId(123);
    book.setTitle("title0");
    book.setAuthor("author0");

    books.put(book.getId(), book);
  }

  @Get("/books")
  public void getBooks(Result<Collection<Book>> result)
  {
    result.ok(books.values());
  }

  @Get("/books/{id}")
  public void getBook(@Path("id") Long id, Result<Book> result)
  {
    result.ok(books.get(id));
  }

  @Post("/books")
  public void addBook(@Body Book book, Result<Void> result)
  {
    books.put(book.getId(), book);

    result.ok(null);
  }

  public static void main(String[] args)
  {
    include(Bookstore.class);

    port(8080);

    start();
  }
}
  1. @Service creates a single-threaded service. Because it is single-threaded, we do not need synchronization and can use a simple HashMap.
  2. @Get("/books") maps the method to a GET request at the URL “/books”.
  3. Then result.ok() completes the asynchronous request.

That’s all there is to it.

JAX-RS Bookstore (RESTEasy)

In JAX-RS, a simple bookstore application looks like:

JAX-RS Bookstore Application
1
2
3
4
@ApplicationPath("/")
public class BookstoreApplication extends Application
{
}
JAX-RS Bookstore
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Path("/")
@Consumes("application/json")
@Produces("application/json")
public class Bookstore
{
  private ConcurrentHashMap<Long,Book> books = new ConcurrentHashMap<Long,Book>();

  public Bookstore()
  {
    Book book = new Book();

    book.setId(123);
    book.setTitle("title0");
    book.setAuthor("author0");

    books.put(book.getId(), book);
  }

  @GET
  @Path("/books")
  public Collection<Book> getBooks()
  {
    return books.values();
  }

  @POST
  @Path("/books")
  public void addBook(Book book)
  {
    books.put(book.getId(), book);
  }

  @GET
  @Path("/book/{id}")
  public Book getBook(@PathParam("id") Long id)
  {
    return books.get(id);
  }
}
web.xml
1
2
3
<web-app xmlns:javaee="http://java.sun.com/xml/ns/javaee"
         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
</web-app>
  1. JAX-RS requires three files: an application class, a path class, and an empty web.xml.
  2. It must use a ConcurrentHashMap because the application is multi-threaded.
  3. It must be deployed in a container like Tomcat or JBoss.

In terms of complexity, JAX-RS is more complicated than Baratine (albeit more configurable). It requires at least 3 files, a servlet container, and the developer needs to mind concurrency issues.

Performance

Even though Baratine is single-threaded, it easily outperforms JAX-RS (RESTEasy) by over 2x. You can scale out Baratine to take advantage of all the CPU cores on your machine and Baratine will scale up linearly - something that you cannot do with JAX-RS.

// best out of 10 runs
$ wrk "http://127.0.0.1:8080/books"

Persistence

Our bookstore example stores the data in memory. What about saving it to a database? Persistence is out of the scope of JAX-RS and it forces you to use another library like JPA. With Baratine, it comes with a document-style persistence layer. No additional libraries and only a trivial change to our bookstore:

Persistent Baratine Bookstore
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
@Data
@Service
public class Bookstore
{
  private Map<Long,Book> books = new HashMap<Long,Book>();

  public Bookstore()
  {
    Book book = new Book();

    book.setId(123);
    book.setTitle("title0");
    book.setAuthor("author0");

    books.put(book.getId(), book);
  }

  @Get("/books")
  public void getBooks(Result<Collection<Book>> result)
  {
    result.ok(books.values());
  }

  @Get("/books/{id}")
  public void getBook(@Path("id") Long id, Result<Book> result)
  {
    result.ok(books.get(id));
  }

  @Post("/books")
  @Modify
  public void addBook(@Body Book book, Result<Void> result)
  {
    books.put(book.getId(), book);

    result.ok(null);
  }

  public static void main(String[] args)
  {
    include(Bookstore.class);

    port(8080);

    start();
  }
}

There are only two changes to our bookstore:

  1. @Data added to the class, to tell Baratine to save this object into its internal reactive database.
  2. @Modify on addBook() to tell Baratine that this object has been modified and that Baratine should add it to the save queue.

Even though the new bookstore is persistent, we still get the same high performance as before because the persistence is asynchronous and bookstore operates mostly in-memory. That is the beauty of Baratine and only possible because Baratine is reactive. There are no comparable platforms out there.