Skip to content

Instantly share code, notes, and snippets.

@minwoox
Last active February 15, 2021 08:44
Show Gist options
  • Save minwoox/5b58e75664bccff29c63cfde080f90f9 to your computer and use it in GitHub Desktop.
Save minwoox/5b58e75664bccff29c63cfde080f90f9 to your computer and use it in GitHub Desktop.
package example.armeria.server.annotated;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.linecorp.armeria.common.AggregatedHttpRequest;
import com.linecorp.armeria.common.HttpRequest;
import com.linecorp.armeria.common.HttpResponse;
import com.linecorp.armeria.common.HttpStatus;
import com.linecorp.armeria.common.MediaType;
import com.linecorp.armeria.server.HttpService;
import com.linecorp.armeria.server.ServiceRequestContext;
import com.linecorp.armeria.server.annotation.Delete;
import com.linecorp.armeria.server.annotation.Get;
import com.linecorp.armeria.server.annotation.Param;
import com.linecorp.armeria.server.annotation.Post;
import com.linecorp.armeria.server.annotation.ProducesJson;
import com.linecorp.armeria.server.annotation.Put;
public class BlogService {
private static final ObjectMapper mapper = new ObjectMapper();
private final AtomicInteger idGenerator = new AtomicInteger();
private final Map<Integer, Blog> blogs = new ConcurrentHashMap<>();
@Post("/blogs")
public HttpResponse createBlog(@Param String title, @Param String content) throws JsonProcessingException {
// Use integer for simplicity.
final int id = idGenerator.getAndIncrement();
final Blog blog = new Blog(id, title, content);
// Use just a map to store the blog. In real world, we should use a database.
// If we use a blocking call to the database, we should use @Blocking.
blogs.put(id, blog);
// We can add additional property such as url: "http://tutorial.com/blogs/1" to respect Rest API.
final byte[] bytes = mapper.writeValueAsBytes(blog);
return HttpResponse.of(HttpStatus.OK, MediaType.JSON_UTF_8, bytes);
}
@Get("/blogs/:id")
public HttpResponse getBlog(@Param int id) throws JsonProcessingException {
final Blog blog = blogs.get(id);
final byte[] content = mapper.writeValueAsBytes(blog);
return HttpResponse.of(HttpStatus.OK, MediaType.JSON_UTF_8, content);
}
@Get("/blogs")
@ProducesJson
public Iterable<Blog> getBlogs() throws JsonProcessingException {
return blogs.values();
}
@Put("/blogs/:id")
public HttpResponse updateBlog(@Param int id, @Param String content) throws JsonProcessingException {
final Blog blog = blogs.get(id);
if (blog == null) {
return HttpResponse.of(HttpStatus.NOT_FOUND);
}
final Blog updated = new Blog(id, blog.title(), content);
blogs.put(id, updated);
final byte[] bytes = mapper.writeValueAsBytes(blog);
return HttpResponse.of(HttpStatus.OK, MediaType.JSON_UTF_8, bytes);
}
@Delete("/blogs/:id")
public HttpResponse removeBlog(@Param int id) {
final Blog removed = blogs.remove(id);
if (removed == null) {
return HttpResponse.of(HttpStatus.NOT_FOUND);
}
return HttpResponse.of(HttpStatus.NO_CONTENT);
}
// Binds to @Put("/blogs/:id") using route API.
private static class UpdateBlogService implements HttpService {
private final Map<Integer, Blog> blogs;
UpdateBlogService(Map<Integer, Blog> blogs) {
this.blogs = blogs;
}
@Override
public HttpResponse serve(ServiceRequestContext ctx, HttpRequest req) throws Exception {
final CompletableFuture<HttpResponse> future = new CompletableFuture<>();
req.aggregate().thenAccept(aggregatedHttpRequest -> {
final int id = Integer.parseInt(ctx.pathParam("id"));
final String content = content(aggregatedHttpRequest);
if (content == null) {
future.completeExceptionally(new IllegalArgumentException("content is not specified."));
}
final Blog blog = blogs.get(id);
if (blog == null) {
future.complete(HttpResponse.of(HttpStatus.NOT_FOUND));
return;
}
final Blog updated = new Blog(id, blog.title(), content);
blogs.put(id, updated);
future.complete(HttpResponse.of(HttpStatus.OK));
});
return HttpResponse.from(future);
}
@Nullable
private static String content(AggregatedHttpRequest aggregatedHttpRequest) {
final JsonNode jsonNode;
try {
jsonNode = mapper.readTree(aggregatedHttpRequest.contentUtf8());
} catch (JsonProcessingException e) {
return null;
}
final JsonNode content = jsonNode.get("content");
if (content == null) {
return null;
}
return content.asText();
}
}
// Add some comment that you can use a library that removes the boiler plates code such as lombok
static final class Blog {
private final int id;
private final String title;
private final String content;
private final long createdAt;
Blog(int id, String title, String content) {
this.id = id;
this.title = title;
this.content = content;
createdAt = System.currentTimeMillis();
}
@JsonProperty("id")
int id() {
return id;
}
@JsonProperty("title")
String title() {
return title;
}
@JsonProperty("content")
String content() {
return content;
}
@JsonProperty("createdAt")
long createdAt() {
return createdAt;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment