Skip to content

Commit

Permalink
Add blocking/db lab
Browse files Browse the repository at this point in the history
  • Loading branch information
danhyun committed Nov 9, 2015
1 parent 183ab13 commit cd8793f
Show file tree
Hide file tree
Showing 31 changed files with 2,071 additions and 13 deletions.
53 changes: 53 additions & 0 deletions lab-07-answer/LAB.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Lab 07 - Blocking lab

So far we have been working with CPU bound code. We have not had to perform any kind of synchronous or blocking actions.
Ratpack is asynchronous and non-blocking from the ground up. Ratpack's APIs are marked as `ratpack.api.NonBlocking` where
appropriate to denote the API's non-blocking and asynchronous nature.

In the real world our existing libraries tend to be blocking and synchronous like the JDBC library.

Ratpack uses a much smaller set of Threads to manage HTTP Request handling, a single thread could be responsible for handling
hundreds of active connections. If you perform a blocking action one of Ratpack's main compute threads you will impede
throughput and prevent other requests from being processed until the blocking code completes.

Ratpack provides a great utility for integrating synchronous/blocking code with Ratpack's Execution Model via
`ratpack.exec.Blocking`. This `Blocking` utility allows users to specify blocking code that will inform Ratpack to
execute the blocking code on Ratpack's blocking scheduler.

Ratpack provides two constructs for representing an asynchronous units of work: `ratpack.exec.Promise` and `ratpack.exec.Operation`.

The `Blocking` utility creates `Promise` and `Operation` from user supplied code which can then be further mapped or consumed.


To complete this lab you will need to implement the `DefaultBookRepository` to perform blocking database calls.
This lab provides generated jOOQ library that specifies a typed representation of the `book` table.

The database connection to an H2 in-memory database is provided via the `ratpack.h2.H2Module` and connection pooling is
made available through `ratpack.hikari.HikariModule`.

## This lab covers

* Working with synchronous/blocking libraries
* Working with Promises and Operations
* Working with Blocking api
* Working with Ratpack's integration with H2 and Hikari
* Parsing incoming JSON
* Rendering a custom object with a different content type depending on what has been requested

## Sign Posts

`ratpack.exec.Promise`

`ratpack.exec.Operation`

`ratpack.exec.Blocking`

`ratpack.hikari.HikariModule`

`ratpack.guice.ConfigurableModule`

`ratpack.handling.Context#clientError(int)`

`ratpack.jackson.Jackson.json(Object)`

`ratpack.handling.Context#parse(Class)`
53 changes: 53 additions & 0 deletions lab-07-answer/lab-07-answer.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import java.nio.file.Paths

buildscript {
repositories {
mavenLocal()
mavenCentral()
}

dependencies {
classpath 'org.jooq:jooq-codegen:3.7.0'
classpath 'com.h2database:h2:1.4.190'
}
}

dependencies {
compile 'org.jooq:jooq:3.7.0'
compile ratpack.dependency('guice')
compile ratpack.dependency('hikari')
compile ratpack.dependency('h2')
}

task generateJooq(group: 'Lab 07 Utilities', description: 'Generates jOOQ library from src/main/resources.init.sql for this lab') << {
// Use your favourite XML builder to construct the code generation configuration file
// ----------------------------------------------------------------------------------
def writer = new StringWriter()
String initSql = Paths.get(projectDir.toString()).resolve('src/main/resources/init.sql').toString().replaceAll('\\\\', '/')
def xml = new groovy.xml.MarkupBuilder(writer)
.configuration('xmlns': 'http://www.jooq.org/xsd/jooq-codegen-3.7.0.xsd') {
jdbc() {
driver('org.h2.Driver')
url("jdbc:h2:mem:lab07;MODE=MySQL;INIT=RUNSCRIPT FROM '$initSql'")
user('sa')
password('')
}
generator() {
database() {
inputSchema('lab07')
}
generate() {
}
target() {
packageName('lab07.jooq')
directory("$projectDir/src/main/java")
}
}
}

// Run the code generator
// ----------------------
org.jooq.util.GenerationTool.generate(
javax.xml.bind.JAXB.unmarshal(new StringReader(writer.toString()), org.jooq.util.jaxb.Configuration.class)
)
}
60 changes: 60 additions & 0 deletions lab-07-answer/src/main/java/lab07/Book.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package lab07;

import java.math.BigDecimal;

public class Book {
private String isbn;
private long quantity;
private BigDecimal price;
private String title;
private String author;
private String publisher;

public String getIsbn() {
return isbn;
}

public void setIsbn(String isbn) {
this.isbn = isbn;
}

public long getQuantity() {
return quantity;
}

public void setQuantity(long quantity) {
this.quantity = quantity;
}

public BigDecimal getPrice() {
return price;
}

public void setPrice(BigDecimal price) {
this.price = price;
}

public String getTitle() {
return title;
}

public void setTitle(String title) {
this.title = title;
}

public String getAuthor() {
return author;
}

public void setAuthor(String author) {
this.author = author;
}

public String getPublisher() {
return publisher;
}

public void setPublisher(String publisher) {
this.publisher = publisher;
}
}
12 changes: 12 additions & 0 deletions lab-07-answer/src/main/java/lab07/BookRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package lab07;

import ratpack.exec.Operation;
import ratpack.exec.Promise;

import java.util.List;

public interface BookRepository {
Operation addBook(Book book);
Promise<List<Book>> getBooks();
Promise<Book> getBook(String isbn);
}
12 changes: 12 additions & 0 deletions lab-07-answer/src/main/java/lab07/BookService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package lab07;

import ratpack.exec.Operation;
import ratpack.exec.Promise;

import java.util.List;

public interface BookService {
Operation addBook(Book book);
Promise<List<Book>> getBooks();
Promise<Book> getBook(String isbn);
}
56 changes: 56 additions & 0 deletions lab-07-answer/src/main/java/lab07/DefaultBookRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package lab07;

import com.google.inject.Inject;
import org.jooq.DSLContext;
import org.jooq.SQLDialect;
import org.jooq.impl.DSL;
import ratpack.exec.Blocking;
import ratpack.exec.Operation;
import ratpack.exec.Promise;

import javax.sql.DataSource;
import java.util.List;

import static lab07.jooq.tables.Book.BOOK;

public class DefaultBookRepository implements BookRepository {

private final DSLContext create;

@Inject
public DefaultBookRepository(DataSource ds) {
create = DSL.using(ds, SQLDialect.MYSQL);
}

@Override
public Operation addBook(Book book) {
// TODO - Implement addBook function
// Hint - checkout Blocking#op(Block) to see how to integrate blocking code with Ratpack
// Hint - checkout DSLContext#newRecord(Table, Object)
// Hint - lab07.jooq.tables.Book.BOOK is the repesentation for the underlying `book` table
return Blocking.op(() -> create.newRecord(BOOK, book).store());
}

@Override
public Promise<List<Book>> getBooks() {
// TODO - Implement getBooks function
// Hint - checkout Blocking#get(Block) to see how to integrate blocking code with Ratpack
// Hint - lab07.jooq.tables.Book.BOOK is the repesentation for the underlying `book` table
// Hint - checkout DSLContext#select() and ResultQuery#fetchInto(Class)
return Blocking.get(() ->
create.select().from(BOOK).fetchInto(Book.class)
);
}

@Override
public Promise<Book> getBook(String isbn) {
// TODO - Implement getBooks function
// Hint - checkout Blocking#get(Block) to see how to integrate blocking code with Ratpack
// Hint - lab07.jooq.tables.Book.BOOK is the repesentation for the underlying `book` table
// Hint - checkout SelectWhereStep#where(Condition...) and ResultQuery#fetchOneInto(Class)
return Blocking.get(() ->
create.select().from(BOOK).where(BOOK.ISBN.equal(isbn)).fetchOneInto(Book.class)
);
}

}
32 changes: 32 additions & 0 deletions lab-07-answer/src/main/java/lab07/DefaultBookService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package lab07;

import ratpack.exec.Operation;
import ratpack.exec.Promise;

import javax.inject.Inject;
import java.util.List;

public class DefaultBookService implements BookService {
final private BookRepository repository;

@Inject
public DefaultBookService(BookRepository bookRepository) {
this.repository = bookRepository;
}


@Override
public Operation addBook(Book book) {
return repository.addBook(book);
}

@Override
public Promise<List<Book>> getBooks() {
return repository.getBooks();
}

@Override
public Promise<Book> getBook(String isbn) {
return repository.getBook(isbn);
}
}
69 changes: 69 additions & 0 deletions lab-07-answer/src/main/java/lab07/Lab07.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package lab07;

import com.google.common.collect.Maps;
import ratpack.guice.Guice;
import ratpack.h2.H2Module;
import ratpack.hikari.HikariModule;
import ratpack.jackson.Jackson;
import ratpack.server.RatpackServer;

import java.util.Map;

public class Lab07 {
public static void main(String[] args) throws Exception {
RatpackServer.start(ratpackServerSpec -> ratpackServerSpec
.registry(Guice.registry(bindingsSpec -> bindingsSpec
.module(new H2Module("sa", "", "jdbc:h2:mem:lab07;MODE=MySQL"))
.module(HikariModule.class, hikariConfig -> {
hikariConfig.addDataSourceProperty("user", "sa");
hikariConfig.addDataSourceProperty("password", "");
hikariConfig.addDataSourceProperty("URL", "jdbc:h2:mem:lab07;INIT=RUNSCRIPT FROM 'classpath:init.sql'");
hikariConfig.setDataSourceClassName("org.h2.jdbcx.JdbcDataSource");
})
.bind(BookRepository.class, DefaultBookRepository.class)
.bind(BookService.class, DefaultBookService.class)
))
.handlers(chain -> chain
.prefix("api/book", apiChain -> apiChain
.get(":isbn", ctx -> {
BookService bookService = ctx.get(BookService.class);
String isbn = ctx.getPathTokens().get("isbn");
// Hint - Checkout ratpack.exec.Promise.then(Action)
// Hint - Use Ratpack's Jackson integration to render the Promised json
// If book doesn't exist return 404
bookService
.getBook(isbn)
.onNull(() -> ctx.clientError(404))
.map(Jackson::json)
.then(ctx::render);
})
.all(ctx -> {
BookService bookService = ctx.get(BookService.class);
ctx.byMethod(byMethodSpec -> byMethodSpec
.get(() ->
// Render the promised list of books as json
// Hint - Checkout ratpack.exec.Promise.then(Action)
// Hint - Use Ratpack's Jackson integration to render the Promised json
bookService.getBooks().map(Jackson::json).then(ctx::render)
)
.post(() ->
// Parse incoming json, store book, then return {"message":"success"}
// Hint - Checkout ratpack.handling.Context#parse(Class)
ctx.parse(Book.class)
.flatMap(book -> bookService
.addBook(book)
.map(() -> {
Map<String, String> result = Maps.newHashMap();
result.put("message", "success");
return result;
}))
.map(Jackson::json)
.then(ctx::render)
)
);
})
)
)
);
}
}
Loading

0 comments on commit cd8793f

Please sign in to comment.