Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Getting Started With Ratpack and Couchbase

DZone's Guide to

Getting Started With Ratpack and Couchbase

Ratpack is non-blocking, asynchronous, and netty-based. So it comes as a natural candidate for a lightweight application framework.

· Database Zone
Free Resource

Traditional relational databases weren’t designed for today’s customers. Learn about the world’s first NoSQL Engagement Database purpose-built for the new era of customer experience.

I recently started playing with Ratpack.  

It's a set of Java libraries for building modern HTTP applications.

To go beyond that simple definition, Ratpack is non-blocking, asynchronous, and netty-based. A bit like our Java SDK. So it comes as a natural candidate for a lightweight application framework.

One of the first things you can do when testing a new web framework is create a basic API. And building a simple CRUD comes naturally when you work with a document database like Couchbase. So this post is a basic intro to Ratpack showcasing the Couchbase entity repository abstraction available with our Java SDK. I will not focus on the API itself here, but more on the mechanism used in Ratpack and the Couchbase repository.

Creating a Ratpack Java Project

Most of the Ratpack users I know tend to code with Groovy. I am usually coding in Java so this is what I am going to use today. The quickstart guides you'll find for Ratpack will feature Groovy though. The lazybones template is a Groovy one for instance. So I started by generating a simple Java Gradle project in my favorite IDE. Then I opened the build.gradle file and completed it with the right dependencies. I added the jcenter repository, a dependency to ratpack-gradle 1.3.3, and made sure I was using the io.ratpack.ratpack-java plugin. And, let's not forget the Couchbase dependency.

buildscript {
  repositories {
    jcenter()
  }
  dependencies {
    classpath "io.ratpack:ratpack-gradle:1.3.3"
  }
}

apply plugin: "io.ratpack.ratpack-java"
apply plugin: 'eclipse'

sourceCompatibility = 1.8
version = '1.0'
jar {
    manifest {
        attributes 'Implementation-Title': 'Ratpack Couchbase Repository Sample',
                   'Implementation-Version': version
    }
}

repositories {
    mavenCentral()
    jcenter()
}

dependencies {
  compile "com.couchbase.client:java-client:2.2.5" 
}

mainClassName = "org.couchbase.devex.Application"

test {
    systemProperties 'property': 'value'
}

The next step is to start the app with a simple Hello World. I have an Application class as you can see on the build file above with the mainClassName option. All I am going to do here for a starter is spin up the Ratpack server and register the '/hello' URL path to return a Hello World message.

Ratpack has Handlers objects. A Handler is a function with a Context as a parameter. You can associate a handler with a path. So here I am going to associate a Handler function to the '/hello' path. To understand Ratpack you have to understand handlers as you will use them in all your applications.

The entrypoint in a Ratpack application is the RatpackServer class. From this class, you can use the static method start. It takes a function with a RatpackServerSpec object as a parameter. This object gives you a fluent API to configure the server. The only thing I need to do here is add my handler. The handlers method takes a function with a Handler Chain as a parameter. From that chain I can create my handler:

package org.couchbase.devex;

import ratpack.server.RatpackServer;

public class Application {

  public static void main(String... args) throws Exception {
    RatpackServer
        .start(server -> server.handlers(chain -> chain.path("hello", ctx -> ctx.render("Hello World!"))));
  }

}

When I start the application and go to localhost:5050/hello, I get my hello message. Moving on to the Couchbase part.

The Couchbase Entity Model

The entity model appeared with version 2.2.x. You can declare an entity easily using the Couchbase annotation @Id. You also need to make sure that your entity class has a public, zero arg constructor and that setters and getters are available for the fields you want in the resulting JSON document. To model a user with a username, age, first name and last name, I use the following User class:

package org.couchbase.devex;

import com.couchbase.client.java.repository.annotation.Field;
import com.couchbase.client.java.repository.annotation.Id;

public class User {

  @Id
  private String username;

  private Integer age;

  @Field("fName")
  private String firstName;

  @Field("lName")
  private String lastName;

  public User() {}

  public User(String username, Integer age, String firstName, String lastName) {
    this.username = username;
    this.age = age;
    this.firstName = firstName;
    this.lastName = lastName;
  }

  public String getUsername() {
    return username;
  }

  public void setUsername(String username) {
    this.username = username;
  }

  public Integer getAge() {
    return age;
  }

  public void setAge(Integer age) {
    this.age = age;
  }

  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }

  @Override
  public String toString() {
    return "User [username=" + username + ", age=" + age + ", firstName=" + firstName + ", lastName=" + lastName
        + "]";
  }

}

The @Id annotation is mandatory and defines the field that will be used as key of your document. You can use the @Field annotation to change the name of the properties in your resulting JSON document. Note also that the @Id field won't be in the JSON.

This Entity class has to be used with the EntityDocument. It's very similar in use with the other document implementations available with the SDK. The following code will create a JSON document in Couchbase:

  User user = new User("ldoguin", 31, "Laurent", "Doguin");
  EntityDocument<User> document = EntityDocument.create(user);
  Repository repo = CouchbaseCluster.create().openBucket().repository();
  repo.upsert(document);

Which creates the following document:

Key: ldoguin

Value: { "lName": "Doguin", "age": 31, "fName": "Laurent" }

This code is currently blocking and synchronous. Is this a bad thing? It depends :) If you are running blocking code in a non-blocking thread, like you would do if it was running in place of the hello world above, then you are blocking everything else. And yes, that would be a bad thing because this thread manages the event loop that handles all the requests. I found this blog post quite useful to understand this.

One of Ratpack's goals is to help you use blocking code in an async, non-blocking handler. Ratpack uses Promise as a way to manage asynchronous code. And it also gives you a Blocking primitive that takes a function as an argument and returns a promise. The function will execute the blocking code. Here I have an example that creates a user when hitting the localhost:5050/create URL and returns it when hitting the localhost:5050/get URL:

  public static void main(String... args) throws Exception {
    RatpackServer.start(server -> server.handlers(chain -> chain.path("create", ctx -> {
      Blocking.get(() -> {
        EntityDocument<User> document = EntityDocument.create(new User("ldoguin", 31, "Laurent", "Doguin"));
        Repository repo = CouchbaseCluster.create().openBucket().repository();
        return repo.upsert(document);
      }).then(entityDoc -> ctx.render("OK"));
    }).path("get", ctx -> {
      Blocking.get(() -> {
        Repository repo = CouchbaseCluster.create().openBucket().repository();
        EntityDocument<User> ldoguin = repo.get("ldoguin", User.class);
        return ldoguin;
      }).then(user -> ctx.render(user.content().toString()));
    })
    ));
  }

Setting aside the fact that this is probably the worst designed API ever, this should give you an idea on how you can run blocking, synchronous code in an async handler with Ratpack. Which is the first step for migrating an existing application to Ratpack.

But then, this example does not really showcase all the goodness that is our RxJava based SDK. So in the next post, I will show you how to use RxJava and Ratpack together with the Async version of the Repository.

Learn how the world’s first NoSQL Engagement Database delivers unparalleled performance at any scale for customer experience innovation that never ends.

Topics:
ratpack ,couchbase

Published at DZone with permission of Laurent Doguin, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}