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) {
    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(tableName = "TODOS") {
  val id: Column 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  RoutingContext.sendToEventBusAndResponse(addr: String, msg: JsonObject) {
  vertx().eventBus().request(addr, msg) { res: AsyncResult> ->
    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 { ctx ->
  val id = ctx.request().getParam("todoId")
  ctx.sendToEventBusAndResponse("todo", json {
    obj(
      "action" to ACTION_GET,
      "id" to id
    )
  })
}

val getTodoList = Handler { ctx ->
  ctx.sendToEventBusAndResponse("todo", json { obj("action" to ACTION_GET_ALL) })
}

val addTodo = Handler { ctx ->
  ctx.sendToEventBusAndResponse("todo", json {
    obj(
      "action" to ACTION_CREATE,
      "data" to ctx.body.getBytes()
    )
  })
}

val updateTodo = Handler { ctx ->
  ctx.sendToEventBusAndResponse("todo", json {
    obj(
      "action" to ACTION_UPDATE,
      "id" to ctx.request().getParam("todoId"),
      "data" to ctx.body.getBytes()
    )
  })
}

val deleteTodo = Handler { ctx ->
  ctx.sendToEventBusAndResponse("todo", json {
    obj(
      "action" to ACTION_DELETE,
      "id" to ctx.request().getParam("todoId")
    )
  })
}

val deleteAll = Handler { ctx ->
  ctx.sendToEventBusAndResponse("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 {
    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  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("todo") { msg ->
      handleMessage(msg)
    }

    todoService = JdbcTodoService(vertx, config)
    todoService.initData()
  }

  private fun handleMessage(msg: Message) {
    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  doAsync(msg: Message, 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?

  suspend fun getCertain(todoID: String): Todo?

  suspend fun update(todoId: String, newTodo: Todo): Todo?

  suspend fun delete(todoId: String): Boolean

  suspend fun deleteAll(): Boolean
}

你可能感兴趣的:(kt-rest)