Implementing CREATE operation

In the previous step, we defined empty service methods. In this step, we'll fill in one of the service methods to create a blog post and implement a corresponding client method. Also, we'll try making a call to the service method with test code using Armeria's ServerExtension.

What you need

You need to have the following files obtained from previous steps. You can always download the full version, instead of creating one yourself.

1. Implement server-side

Let's implement a service method to create a blog post.

  1. In the BlogServiceImpl class, create an ID generator to issue temporary blog post IDs and a map to contain blog posts.

    BlogServiceImpl.java
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class BlogServiceImpl implements BlogService.AsyncIface {
      private final AtomicInteger idGenerator = new AtomicInteger();
      private final Map<Integer, BlogPost> blogPosts = new ConcurrentHashMap<>();
      ...
    }
  2. In the createBlogPost() method, create a BlogPost object with a generated ID and request parameters.

    BlogServiceImpl.java
    import java.time.Instant;
    ...
    @Override
    public void createBlogPost(CreateBlogPostRequest request, AsyncMethodCallback<BlogPost> resultHandler)
            throws TException {
      final int id = idGenerator.getAndIncrement();
      final Instant now = Instant.now();
      final BlogPost blogPost = new BlogPost()
              .setId(id)
              .setTitle(request.getTitle())
              .setContent(request.getContent())
              .setModifiedAt(now.toEpochMilli())
              .setCreatedAt(now.toEpochMilli());
    }
  3. In the createBlogPost() method, save the post information in the blogPosts map and return the information of the created blog post to the resultHandler.

    BlogServiceImpl.java
    @Override
    public void createBlogPost(CreateBlogPostRequest request, AsyncMethodCallback<BlogPost> resultHandler)
            throws TException {
      ...
      blogPosts.put(id, blogPost);
      final BlogPost stored = blogPost;
      resultHandler.onComplete(stored);
    }

2. Create a client

Let's create a client to send a request to the service. You can refer to the full version of the file here of the file.

  1. Create a class for our client. We'll name the class BlogClient.

    BlogClient.java
    package example.armeria.server.blog.thrift;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class BlogClient {
      private static final Logger logger = LoggerFactory.getLogger(BlogClient.class);
    }
  2. In the BlogClient class, add a constructor and create a Thrift client instance using Armeria's ThriftClients.

    BlogClient.java
    import java.net.URI;
    import com.linecorp.armeria.client.thrift.ThriftClients;
    import example.armeria.blog.thrift.BlogService;
    
    public class BlogClient {
      ...
      private final BlogService.Iface blogService;
    
      BlogClient(URI uri, String path) {
        blogService =
          ThriftClients.builder(uri)
            .path(path)
            .build(BlogService.Iface.class);
      }
    }

3. Implement client-side

In the BlogClient class, add a method to send a request to create a blog post.

BlogClient.java
import org.apache.thrift.TException;
import example.armeria.blog.thrift.BlogPost;
import example.armeria.blog.thrift.CreateBlogPostRequest;
...
BlogPost createBlogPost(String title, String content) throws TException {
  final CreateBlogPostRequest request =
          new CreateBlogPostRequest().setTitle(title)
                                     .setContent(content);
  return blogService.createBlogPost(request);
}

4. Create a test file

Let's start writing test code. We'll use test code to verify what we implement along the way.

Create a file, BlogServiceTest.java, under {project_root}/src/test/java/example/armeria/server/blog/thrift as follows. You can see the full version of the file here.

BlogServiceTest.java
package example.armeria.server.blog.thrift;

class BlogServiceTest {}

5. Register a ServerExtension

Armeria's ServerExtension automatically handles set-up and tear-down of a server for testing. This is convenient as it eliminates the need to execute the main method to set up a server before running our tests.

In the BlogServiceTest class, register a ServerExtension as follows. Note that the service instance is added to the configuration.

BlogServiceTest.java
import org.junit.jupiter.api.extension.RegisterExtension;
import com.linecorp.armeria.server.ServerBuilder;
import com.linecorp.armeria.server.thrift.THttpService;
import com.linecorp.armeria.testing.junit5.server.ServerExtension;
...
@RegisterExtension
static final ServerExtension server = new ServerExtension() {
  @Override
  protected void configure(ServerBuilder sb) throws Exception {
    sb.service("/thrift",
               THttpService.builder()
                           // Add the service to the configuration
                           .addService(new BlogServiceImpl())
                           .build());
  }
};

6. Test creating a blog post

Let's test if we can create a blog post.

  1. In the BlogServiceTest class, add a test method as follows.

    BlogServiceTest.java
    import static org.assertj.core.api.Assertions.assertThat;
    import org.apache.thrift.TException;
    import org.junit.jupiter.api.Test;
    import example.armeria.blog.thrift.BlogPost;
    ...
    @Test
    void createBlogPost() throws TException {
      final BlogClient client = new BlogClient(server.httpUri(), "/thrift");
      final BlogPost response = client.createBlogPost("My first blog", "Hello Armeria!");
      assertThat(response.getId()).isGreaterThanOrEqualTo(0);
      assertThat(response.getTitle()).isEqualTo("My first blog");
      assertThat(response.getContent()).isEqualTo("Hello Armeria!");
    }
  2. Run the test case on your IDE or using Gradle.

    ./gradlew test

    The service worked as expected if you see the test case passed.

What's next

In this step, we've implemented a method for creating a blog post. We've also written test code and registered ServerExtension to our test.

Next, at Step 4. Implement READ, we'll implement a READ operation to read a single post and also multiple posts.