buildscript {
repositories {
mavenLocal()
maven {
url "https://www.artifactrepository.citigroup.net:443/artifactory/maven-icg-dev"
credentials {
username = "ocean-devops"
password = "APBQW78wqY4oewwXFBtTfcN17ZG"
}
}
maven {
url "https://www.artifactrepository.citigroup.net:443/artifactory/maven-dev"
credentials {
username = "ocean-devops"
password = "APBQW78wqY4oewwXFBtTfcN17ZG"
}
}
}
ext {
kt_version = "1.3.40"
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kt_version}")
classpath "org.jetbrains.kotlin:kotlin-allopen:${kt_version}"
classpath "org.jetbrains.kotlin:kotlin-noarg:${kt_version}"
// classpath("org.flywaydb:flyway-gradle-plugin:4.0.3")
// classpath("gradle.plugin.io.vertx:vertx-gradle-plugin:0.8.0")
}
}
//plugins {
// id 'java'
//// id 'org.jetbrains.kotlin.jvm' version '1.3.40'
//}
apply plugin: 'java'
apply plugin: 'kotlin'
//apply plugin: "kotlin-allopen"
//apply plugin: "kotlin-noarg"
//apply plugin: "io.vertx.vertx-plugin"
group 'com.wh'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
ext {
kt_version = "1.3.40"
vt_version = "3.8.2"
ktorm_version = "2.5"
h2_version = "1.4.199"
}
repositories {
mavenLocal()
maven {
url = uri("https://www.artifactrepository.citigroup.net:443/artifactory/maven-icg-dev")
credentials {
username = "ocean-devops"
password = "APBQW78wqY4oewwXFBtTfcN17ZG"
}
}
mavenCentral()
jcenter()
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${kt_version}"
implementation "org.jetbrains.kotlin:kotlin-reflect:${kt_version}"
implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.10"
// vertx
annotationProcessor("io.vertx:vertx-codegen:$vt_version:processor")
implementation("io.vertx:vertx-core:${vt_version}")
implementation("io.vertx:vertx-web:$vt_version")
implementation("io.vertx:vertx-lang-kotlin:$vt_version")
implementation("io.vertx:vertx-lang-kotlin-coroutines:$vt_version")
implementation("io.vertx:vertx-jdbc-client:$vt_version")
implementation("io.vertx:vertx-redis-client:${vt_version}")
// db
// java -cp .\h2-1.4.199.jar org.h2.tools.Server -tcp -tcpAllowOthers -web -webAllowOthers -browser -ifNotExists
implementation("com.h2database:h2:$h2_version")
implementation("me.liuwj.ktorm:ktorm-core:$ktorm_version")
testImplementation("io.vertx:vertx-unit:$vt_version")
testCompile group: 'junit', name: 'junit', version: '4.12'
}
sourceSets {
main {
java {
srcDirs += 'src/main/generated'
}
}
}
// compileOnly requires Gradle 2.12+
task annotationProcessing(type: JavaCompile, group: 'build') {
description 'Generates Vertx js and rxjava shims'
source = sourceSets.main.java
classpath = configurations.compileClasspath + configurations.runtimeClasspath
destinationDir = project.file('src/main/generated')
options.annotationProcessorPath = configurations.annotationProcessor
options.compilerArgs = [
"-proc:only",
"-processor", "io.vertx.codegen.CodeGenProcessor",
"-Acodegen.output=${destinationDir.absolutePath}"
]
}
compileJava {
targetCompatibility = 1.8
sourceCompatibility = 1.8
dependsOn annotationProcessing
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
jar {
// by default fat jar
archiveName = 'kt-rest-todo-backend-fat.jar'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
manifest {
attributes 'Main-Class': 'io.vertx.core.Launcher'
attributes 'Main-Verticle': 'io.vertx.blueprint.todolist.verticle.RxTodoVerticle'
}
}
gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=file\:/c:/Users/hw83770/.gradle/wrapper/dists/gradle-5.5.1-bin.zip
systemProp.http.proxyHost=natcpproxy.wlb2.nam.nsroot.net
systemProp.http.proxyPort=7777
systemProp.http.proxyUser=hw83770
systemProp.http.proxyPassword=Hw190625
systemProp.http.nonProxyHosts=localhost|*.nsroot.net|*.ssmb.com|*.citigroup.net
org.gradle.warning.mode=all
docker-compose.yml
version: "2"
services:
redis:
container_name: vertx-kt-res-redis
image: redis:latest
expose:
- "6379"
vertx-todo-backend:
depends_on:
- redis
container_name: vertx-kt-rest
build: .
links:
- redis
ports:
- "8082:8082"
Dockfile
FROM java:8-jre
ENV VERTICLE_FILE build/libs/vertx-blueprint-todo-backend-fat.jar
# Set the location of the verticles
ENV VERTICLE_HOME /usr/verticles
EXPOSE 8082
COPY $VERTICLE_FILE $VERTICLE_HOME/
COPY config/config_docker.json $VERTICLE_HOME/
WORKDIR $VERTICLE_HOME
ENTRYPOINT ["sh", "-c"]
CMD ["java -jar vertx-blueprint-todo-backend-fat.jar -conf config_docker.json"]
import io.vertx.core.AbstractVerticle
import io.vertx.core.DeploymentOptions
import io.vertx.core.Vertx
import io.vertx.core.VertxOptions
import io.vertx.kotlin.core.json.json
import io.vertx.kotlin.core.json.obj
import todolist.TodoRestVerticle
import todolist.service.ServiceVerticle
/**
* @Author hw83770
* @Date 11:04 2019/10/15
*
*/
object Starter {
private val vertxOptions by lazy {
createOption()
}
@JvmStatic
fun main(args: Array<String>) {
val vertex = Vertx.vertx(vertxOptions)
val config = json {
obj(
"service.type" to "jdbc",
"url" to "jdbc:h2:tcp://localhost/~/Documents/db/kt_rest",
"driver_class" to "org.h2.Driver",
"user" to "sa",
"password" to "",
"max_pool_size" to 30,
"http.port" to 9991
)
}
vertex.deployVerticle(ServiceVerticle(), DeploymentOptions().setWorker(true).setConfig(config))
vertex.deployVerticle(TodoRestVerticle(), DeploymentOptions().setConfig(config))
}
private fun createOption(): VertxOptions {
return VertxOptions().apply {
eventLoopPoolSize = 4
workerPoolSize = 4
}
}
}
package todolist
import io.vertx.core.AbstractVerticle
import io.vertx.core.logging.LoggerFactory
import todolist.service.JdbcTodoService
import todolist.service.TodoService
/**
* @Author hw83770
* @Date 11:12 2019/10/15
*
*/
class TodoRestVerticle : AbstractVerticle() {
private val logger = LoggerFactory.getLogger(TodoRestVerticle::class.java)
val router by lazy {
createRouter(vertx)
}
val defaultPort = 8080
val service: TodoService by lazy {
createService()
}
override fun start() {
val port = config().getInteger("http.port", defaultPort)
vertx.createHttpServer().requestHandler(router).listen(port)
}
private fun createService(): TodoService {
return JdbcTodoService(vertx, config())
}
}
package todolist
import me.liuwj.ktorm.dsl.QueryRowSet
import me.liuwj.ktorm.schema.*
/**
* @Author hw83770
* @Date 11:30 2019/10/15
*
*/
data class Todo(
val id: Int,
val title: String,
var completed: Boolean,
var order: Int,
var url: String
)
object TODOS : BaseTable<Todo>(tableName = "TODOS") {
val id: Column<Int> by int("ID").primaryKey()
val title by varchar("TITLE")
val completed by boolean("COMPLETED")
val order by int("ORDER")
val url by varchar("URL")
override fun doCreateEntity(row: QueryRowSet, withReferences: Boolean) = Todo(
id = row[id] ?: 0,
title = row[title].orEmpty(),
completed = row[completed] ?: false,
order = row[order] ?: 0,
url = row[url].orEmpty()
)
}
package todolist
import io.vertx.core.AsyncResult
import io.vertx.core.Handler
import io.vertx.core.http.HttpMethod.*
import io.vertx.core.Vertx
import io.vertx.core.eventbus.Message
import io.vertx.core.http.HttpHeaders.CONTENT_TYPE
import io.vertx.core.http.HttpHeaders.ORIGIN
import io.vertx.core.json.Json
import io.vertx.core.json.JsonArray
import io.vertx.core.json.JsonObject
import io.vertx.ext.web.Router
import io.vertx.ext.web.RoutingContext
import io.vertx.ext.web.handler.BodyHandler
import io.vertx.ext.web.handler.CorsHandler
import io.vertx.kotlin.core.json.json
import io.vertx.kotlin.core.json.obj
/**
* @Author hw83770
* @Date 11:16 2019/10/15
*
*/
fun createRouter(vertx: Vertx): Router = Router.router(vertx).apply {
route().handler(cors)
route().handler(BodyHandler.create())
get(API_GET).handler(getTodo)
get(API_LIST_ALL).handler(getTodoList)
post(API_CREATE).handler(addTodo)
post(API_UPDATE).handler(updateTodo)
post(API_DELETE).handler(deleteTodo)
post(API_DELETE_ALL).handler(deleteAll)
}
fun <T> RoutingContext.sendToEventBusAndResponse(addr: String, msg: JsonObject) {
vertx().eventBus().request(addr, msg) { res: AsyncResult<Message<T>> ->
when {
res.succeeded() -> toJson(res.result().body())
res.failed() -> fail(500, res.cause())
}
}
}
fun RoutingContext.toJson(obj: Any?) {
response().putHeader(CONTENT_TYPE, "application/json; charset=utf-8")
.end(if (obj is String) obj else Json.encodePrettily(obj))
}
// handlers
val cors: CorsHandler = CorsHandler.create("*")
.allowedHeaders(setOf(CONTENT_TYPE.toString(), ORIGIN.toString(), "Access-Control-Allow-Origin"))
.allowedMethods(setOf(GET, POST, PUT, PATCH, DELETE, OPTIONS))
val getTodo = Handler<RoutingContext> { ctx ->
val id = ctx.request().getParam("todoId")
ctx.sendToEventBusAndResponse<Todo>("todo", json {
obj(
"action" to ACTION_GET,
"id" to id
)
})
}
val getTodoList = Handler<RoutingContext> { ctx ->
ctx.sendToEventBusAndResponse<String>("todo", json { obj("action" to ACTION_GET_ALL) })
}
val addTodo = Handler<RoutingContext> { ctx ->
ctx.sendToEventBusAndResponse<String>("todo", json {
obj(
"action" to ACTION_CREATE,
"data" to ctx.body.getBytes()
)
})
}
val updateTodo = Handler<RoutingContext> { ctx ->
ctx.sendToEventBusAndResponse<String>("todo", json {
obj(
"action" to ACTION_UPDATE,
"id" to ctx.request().getParam("todoId"),
"data" to ctx.body.getBytes()
)
})
}
val deleteTodo = Handler<RoutingContext> { ctx ->
ctx.sendToEventBusAndResponse<String>("todo", json {
obj(
"action" to ACTION_DELETE,
"id" to ctx.request().getParam("todoId")
)
})
}
val deleteAll = Handler<RoutingContext> { ctx ->
ctx.sendToEventBusAndResponse<String>("todo", json {
obj(
"action" to ACTION_DELETE_ALL
)
})
}
package todolist
import io.vertx.ext.jdbc.JDBCClient
/**
* @Author hw83770
* @Date 11:46 2019/10/15
*
*/
//fun createDBClient(vertx, config): JDBCClient = JDBCClient.createShared(vertx, config)
const val API_GET = "/todos/:todoId"
const val API_LIST_ALL = "/todos"
const val API_CREATE = "/todos"
const val API_UPDATE = "/todos/:todoId"
const val API_DELETE = "/todos/:todoId"
const val API_DELETE_ALL = "/todos"
const val ACTION_GET = "get"
const val ACTION_GET_ALL = "get_all"
const val ACTION_CREATE = "create"
const val ACTION_UPDATE = "update"
const val ACTION_DELETE = "delete"
const val ACTION_DELETE_ALL = "delete_all"
package todolist.service
import io.vertx.core.Vertx
import io.vertx.core.json.JsonObject
import io.vertx.ext.jdbc.JDBCClient
import io.vertx.kotlin.ext.sql.executeAwait
import io.vertx.kotlin.ext.sql.getConnectionAwait
import kotlinx.coroutines.suspendCancellableCoroutine
import me.liuwj.ktorm.database.Database
import me.liuwj.ktorm.dsl.*
import me.liuwj.ktorm.entity.findAll
import me.liuwj.ktorm.entity.findById
import todolist.TODOS
import todolist.Todo
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
/**
* @Author hw83770
* @Date 11:40 2019/10/15
*
*/
class JdbcTodoService(private val vertx: Vertx, private val config: JsonObject) : TodoService {
val dbClient: JDBCClient by lazy { JDBCClient.createShared(vertx, config) }
lateinit var database: Database
override suspend fun initData(): Boolean {
// val con = dbClient.getConnectionAwait()
// con.executeAwait(SQL_CREATE)
// con.close()
suspendEvent {
database = Database.connect(
url = config.getString("url"),
driver = config.getString("driver_class"),
user = config.getString("user"),
password = config.getString("password")
)
}
return true
}
override suspend fun insert(todo: Todo): Todo? {
val res = suspendEvent {
TODOS.insert {
TODOS.title to todo.title
TODOS.completed to todo.completed
TODOS.order to todo.order
TODOS.url to todo.url
}
}
return if (res > 0) todo else null
}
override suspend fun getAll(): List<Todo> {
return suspendEvent {
TODOS.findAll()
}
}
override suspend fun getCertain(todoID: String): Todo? {
return suspendEvent { TODOS.findById(todoID.toInt()) }
}
override suspend fun update(todoId: String, newTodo: Todo): Todo? {
val res = suspendEvent {
TODOS.update {
it.title to newTodo.title
it.completed to newTodo.completed
it.order to newTodo.order
it.url to newTodo.url
where { it.id eq todoId.toInt() }
}
}
return if (res > 0) newTodo else null
}
override suspend fun delete(todoId: String): Boolean {
return suspendEvent {
TODOS.delete { it.id eq todoId.toInt() }
} > 0
}
override suspend fun deleteAll(): Boolean {
return suspendEvent {
TODOS.deleteAll()
} > 0
}
// 同步方法 转化为协程suspend方法
private suspend fun <T> suspendEvent(block: () -> T): T {
return suspendCancellableCoroutine { continuation ->
try {
continuation.resume(block.invoke())
} catch (e: java.lang.Exception) {
continuation.resumeWithException(e)
}
}
}
private val SQL_CREATE = "CREATE TABLE IF NOT EXISTS `TODOS` (\n" +
" `id` int(11) NOT NULL AUTO_INCREMENT,\n" +
" `title` varchar(255) DEFAULT NULL,\n" +
" `completed` tinyint(1) DEFAULT NULL,\n" +
" `order` int(11) DEFAULT NULL,\n" +
" `url` varchar(255) DEFAULT NULL,\n" +
" PRIMARY KEY (`id`) )"
}
package todolist.service
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import io.vertx.core.eventbus.Message
import io.vertx.core.json.Json
import io.vertx.core.json.JsonObject
import io.vertx.core.logging.LoggerFactory
import io.vertx.kotlin.coroutines.CoroutineVerticle
import io.vertx.kotlin.coroutines.dispatcher
import kotlinx.coroutines.launch
import todolist.*
/**
* @Author hw83770
* @Date 15:16 2019/10/15
*
*/
class ServiceVerticle : CoroutineVerticle() {
lateinit var todoService: TodoService
val mapper = jacksonObjectMapper()
private val logger = LoggerFactory.getLogger(ServiceVerticle::class.java)
override suspend fun start() {
vertx.eventBus().consumer<JsonObject>("todo") { msg ->
handleMessage(msg)
}
todoService = JdbcTodoService(vertx, config)
todoService.initData()
}
private fun handleMessage(msg: Message<JsonObject>) {
val action = msg.body().getString("action")
when (action) {
ACTION_GET -> doAsync(msg) { todoService.getCertain(msg.body().getString("id")) }
ACTION_GET_ALL -> doAsync(msg) { todoService.getAll() }
ACTION_CREATE -> doAsync(msg) {
val obj: ByteArray = msg.body().getBinary("data")
val todo: Todo = mapper.readValue(obj)
todoService.insert(todo)
}
ACTION_UPDATE -> doAsync(msg) {
val id = msg.body().getString("id")
val obj: ByteArray = msg.body().getBinary("data")
val todo: Todo = mapper.readValue(obj)
todoService.update(id, todo)
}
ACTION_DELETE -> doAsync(msg) { todoService.delete(msg.body().getString("id")) }
ACTION_DELETE_ALL -> doAsync(msg) { todoService.deleteAll() }
}
}
fun <T> doAsync(msg: Message<JsonObject>, block: suspend () -> T) {
launch(vertx.dispatcher()) {
val t = block.invoke()
msg.reply(Json.encodePrettily(t))
}
}
// fun Route.coroutineHandler(fn: suspend (RoutingContext) -> Unit) {
// handler { ctx ->
// launch(ctx.vertx().dispatcher()) {
// try {
// fn(ctx)
// } catch (e: Exception) {
// ctx.fail(e)
// }
// }
// }
// }
}
package todolist.service
import todolist.Todo
/**
* @Author hw83770
* @Date 11:36 2019/10/15
*
*/
interface TodoService {
suspend fun initData(): Boolean
suspend fun insert(todo: Todo): Todo?
suspend fun getAll(): List<Todo>?
suspend fun getCertain(todoID: String): Todo?
suspend fun update(todoId: String, newTodo: Todo): Todo?
suspend fun delete(todoId: String): Boolean
suspend fun deleteAll(): Boolean
}