kt-rest

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
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容