THRIFT-5548: add kotlin code gen
Client: kotlin
Patch: Jiayu Liu
This closes #2556
diff --git a/lib/kotlin/cross-test-server/build.gradle.kts b/lib/kotlin/cross-test-server/build.gradle.kts
new file mode 100644
index 0000000..6b20de1
--- /dev/null
+++ b/lib/kotlin/cross-test-server/build.gradle.kts
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+plugins {
+ kotlin("jvm") version "1.5.31"
+ id("com.ncorti.ktfmt.gradle") version "0.4.0"
+ java
+ application
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
+ implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
+ // https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-jdk8
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.6.1")
+ // https://mvnrepository.com/artifact/org.apache.thrift/libthrift
+ implementation("org.apache.thrift:libthrift:INCLUDED")
+ // https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
+ implementation("ch.qos.logback:logback-classic:1.3.0-alpha14")
+ testImplementation("org.jetbrains.kotlin:kotlin-test")
+ testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
+}
+
+tasks {
+ application {
+ applicationName = "TestServer"
+ mainClass.set("org.apache.thrift.test.TestServerKt")
+ }
+
+ ktfmt {
+ kotlinLangStyle()
+ }
+
+ task<Exec>("compileThrift") {
+ val thriftBin = if (hasProperty("thrift.compiler")) {
+ file(property("thrift.compiler"))
+ } else {
+ project.rootDir.resolve("../../compiler/cpp/thrift")
+ }
+ val outputDir = layout.buildDirectory.dir("generated-sources")
+ doFirst {
+ mkdir(outputDir)
+ }
+ commandLine = listOf(
+ thriftBin.absolutePath,
+ "-gen",
+ "kotlin",
+ "-out",
+ outputDir.get().toString(),
+ project.rootDir.resolve("../../test/ThriftTest.thrift").absolutePath
+ )
+ group = LifecycleBasePlugin.BUILD_GROUP
+ }
+
+ compileKotlin {
+ dependsOn("compileThrift")
+ }
+}
+
+sourceSets["main"].java {
+ srcDir(layout.buildDirectory.dir("generated-sources"))
+}
diff --git a/lib/kotlin/cross-test-server/src/main/kotlin/org/apache/thrift/test/TestHandler.kt b/lib/kotlin/cross-test-server/src/main/kotlin/org/apache/thrift/test/TestHandler.kt
new file mode 100644
index 0000000..4bbdb6a
--- /dev/null
+++ b/lib/kotlin/cross-test-server/src/main/kotlin/org/apache/thrift/test/TestHandler.kt
@@ -0,0 +1,260 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.thrift.test
+
+import java.nio.ByteBuffer
+import kotlinx.coroutines.delay
+import org.apache.thrift.TException
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import thrift.test.Insanity
+import thrift.test.Numberz
+import thrift.test.ThriftTest
+import thrift.test.Xception
+import thrift.test.Xception2
+import thrift.test.Xtruct
+import thrift.test.Xtruct2
+
+class TestHandler : ThriftTest {
+
+ companion object {
+ private val logger: Logger = LoggerFactory.getLogger(TestHandler::class.java)
+ }
+
+ override suspend fun testVoid() {
+ logger.info("testVoid()\n")
+ }
+
+ override suspend fun testString(thing: String): String {
+ logger.info("testString(\"$thing\")\n")
+ return thing
+ }
+
+ override suspend fun testBool(thing: Boolean): Boolean {
+ logger.info("testBool($thing)\n")
+ return thing
+ }
+
+ override suspend fun testByte(thing: Byte): Byte {
+ logger.info("testByte($thing)\n")
+ return thing
+ }
+
+ override suspend fun testI32(thing: Int): Int {
+ logger.info("testI32($thing)\n")
+ return thing
+ }
+
+ override suspend fun testI64(thing: Long): Long {
+ logger.info("testI64($thing)\n")
+ return thing
+ }
+
+ override suspend fun testDouble(thing: Double): Double {
+ logger.info("testDouble($thing)\n")
+ return thing
+ }
+
+ override suspend fun testBinary(thing: ByteArray): ByteArray {
+ val buffer = ByteBuffer.wrap(thing)
+ val sb = StringBuilder(buffer.remaining() * 3)
+ buffer.mark()
+ var limit = 0 // limit output to keep the log size sane
+ while (buffer.remaining() > 0 && ++limit < 1024) {
+ sb.append(String.format("%02X ", buffer.get()))
+ }
+ if (buffer.remaining() > 0) {
+ sb.append("...") // indicate we have more date
+ }
+ logger.info("testBinary($sb)\n")
+ buffer.reset()
+ return buffer.array()
+ }
+
+ override suspend fun testStruct(thing: Xtruct): Xtruct {
+ logger.info(
+ """
+testStruct({"${thing.string_thing}", ${thing.byte_thing}, ${thing.i32_thing}, ${thing.i64_thing}})
+
+""".trimIndent()
+ )
+ return thing
+ }
+
+ override suspend fun testNest(thing: Xtruct2): Xtruct2 {
+ val thing2: Xtruct = thing.struct_thing!!
+ logger.info(
+ """
+testNest({${thing.byte_thing}, {"${thing2.string_thing}", ${thing2.byte_thing}, ${thing2.i32_thing}, ${thing2.i64_thing}}, ${thing.i32_thing}})
+
+""".trimIndent()
+ )
+ return thing
+ }
+
+ override suspend fun testMap(thing: Map<Int, Int>): Map<Int, Int> {
+ logger.info("testMap({")
+ logger.info("{}", thing)
+ logger.info("})\n")
+ return thing
+ }
+
+ override suspend fun testStringMap(thing: Map<String, String>): Map<String, String> {
+ logger.info("testStringMap({")
+ logger.info("{}", thing)
+ logger.info("})\n")
+ return thing
+ }
+
+ override suspend fun testSet(thing: Set<Int>): Set<Int> {
+ logger.info("testSet({")
+ var first = true
+ for (elem in thing) {
+ if (first) {
+ first = false
+ } else {
+ logger.info(", ")
+ }
+ logger.info("{}", elem)
+ }
+ logger.info("})\n")
+ return thing
+ }
+
+ override suspend fun testList(thing: List<Int>): List<Int> {
+ logger.info("testList({")
+ var first = true
+ for (elem in thing) {
+ if (first) {
+ first = false
+ } else {
+ logger.info(", ")
+ }
+ logger.info("{}", elem)
+ }
+ logger.info("})\n")
+ return thing
+ }
+
+ override suspend fun testEnum(thing: Numberz): Numberz {
+ logger.info("testEnum($thing)\n")
+ return thing
+ }
+
+ override suspend fun testTypedef(thing: Long): Long {
+ logger.info("testTypedef($thing)\n")
+ return thing
+ }
+
+ override suspend fun testMapMap(hello: Int): Map<Int, Map<Int, Int>> {
+ logger.info("testMapMap($hello)\n")
+ val mapmap: MutableMap<Int, Map<Int, Int>> = HashMap()
+ val pos = HashMap<Int, Int>()
+ val neg = HashMap<Int, Int>()
+ for (i in 1..4) {
+ pos[i] = i
+ neg[-i] = -i
+ }
+ mapmap[4] = pos
+ mapmap[-4] = neg
+ return mapmap
+ }
+
+ override suspend fun testInsanity(argument: Insanity): Map<Long, Map<Numberz, Insanity>> {
+ logger.info("testInsanity()\n")
+ val firstMap = mutableMapOf<Numberz, Insanity>()
+ val secondMap = mutableMapOf<Numberz, Insanity>()
+ firstMap[Numberz.TWO] = argument
+ firstMap[Numberz.THREE] = argument
+ val looney = Insanity()
+ secondMap[Numberz.SIX] = looney
+ val insane: MutableMap<Long, Map<Numberz, Insanity>> = HashMap()
+ insane[1L] = firstMap
+ insane[2L] = secondMap
+ return insane
+ }
+
+ override suspend fun testMulti(
+ arg0: Byte,
+ arg1: Int,
+ arg2: Long,
+ arg3: Map<Short, String>,
+ arg4: Numberz,
+ arg5: Long
+ ): Xtruct {
+ logger.info("testMulti()\n")
+ val hello = Xtruct()
+ hello.string_thing = "Hello2"
+ hello.byte_thing = arg0
+ hello.i32_thing = arg1
+ hello.i64_thing = arg2
+ return hello
+ }
+
+ @Throws(Xception::class, TException::class)
+ override suspend fun testException(arg: String) {
+ logger.info("testException($arg)\n")
+ when (arg) {
+ "Xception" -> {
+ val x = Xception()
+ x.errorCode = 1001
+ x.setFieldValue(Xception._Fields.MESSAGE, arg)
+ throw x
+ }
+ "TException" -> {
+ // Unspecified exception should yield a TApplicationException on client side
+ throw RuntimeException(arg)
+ }
+ else -> {
+ val result = Xtruct()
+ result.string_thing = arg
+ }
+ }
+ return
+ }
+
+ @Throws(Xception::class, Xception2::class)
+ override suspend fun testMultiException(arg0: String, arg1: String): Xtruct {
+ logger.info("testMultiException($arg0, $arg1)\n")
+ if (arg0 == "Xception") {
+ val x = Xception()
+ x.errorCode = 1001
+ x.setFieldValue(Xception._Fields.MESSAGE, "This is an Xception")
+ throw x
+ } else if (arg0 == "Xception2") {
+ val x = Xception2()
+ x.errorCode = 2002
+ x.struct_thing = Xtruct().apply { string_thing = "This is an Xception2" }
+ throw x
+ }
+ val result = Xtruct()
+ result.string_thing = arg1
+ return result
+ }
+
+ override suspend fun testOneway(secondsToSleep: Int) {
+ logger.info("testOneway($secondsToSleep) => sleeping...")
+ try {
+ delay(secondsToSleep * 1000L)
+ logger.info("Done sleeping!")
+ } catch (ie: InterruptedException) {
+ throw RuntimeException(ie)
+ }
+ }
+}
diff --git a/lib/kotlin/cross-test-server/src/main/kotlin/org/apache/thrift/test/TestServer.kt b/lib/kotlin/cross-test-server/src/main/kotlin/org/apache/thrift/test/TestServer.kt
new file mode 100644
index 0000000..b04548d
--- /dev/null
+++ b/lib/kotlin/cross-test-server/src/main/kotlin/org/apache/thrift/test/TestServer.kt
@@ -0,0 +1,358 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.thrift.test
+
+import kotlin.system.exitProcess
+import kotlinx.coroutines.GlobalScope
+import org.apache.thrift.TException
+import org.apache.thrift.TMultiplexedProcessor
+import org.apache.thrift.protocol.TBinaryProtocol
+import org.apache.thrift.protocol.TCompactProtocol
+import org.apache.thrift.protocol.TJSONProtocol
+import org.apache.thrift.protocol.TProtocol
+import org.apache.thrift.protocol.TProtocolFactory
+import org.apache.thrift.server.ServerContext
+import org.apache.thrift.server.TNonblockingServer
+import org.apache.thrift.server.TServer
+import org.apache.thrift.server.TServerEventHandler
+import org.apache.thrift.server.TSimpleServer
+import org.apache.thrift.server.TThreadPoolServer
+import org.apache.thrift.server.TThreadedSelectorServer
+import org.apache.thrift.transport.TNonblockingServerSocket
+import org.apache.thrift.transport.TNonblockingServerSocket.NonblockingAbstractServerSocketArgs
+import org.apache.thrift.transport.TSSLTransportFactory
+import org.apache.thrift.transport.TServerSocket
+import org.apache.thrift.transport.TServerSocket.ServerSocketTransportArgs
+import org.apache.thrift.transport.TTransport
+import org.apache.thrift.transport.TTransportFactory
+import org.apache.thrift.transport.TZlibTransport
+import org.apache.thrift.transport.layered.TFastFramedTransport
+import org.apache.thrift.transport.layered.TFramedTransport
+import thrift.test.SecondService
+import thrift.test.SecondServiceProcessor
+import thrift.test.ThriftTestProcessor
+
+object TestServer {
+
+ // Multiplexed Protocol Support Details:
+ //
+ // For multiplexed testing we always use binary protocol underneath.
+ //
+ // "ThriftTest" named service implements "ThriftTest" from ThriftTest.thrift
+ // "SecondService" named service implements "SecondService" from ThriftTest.thrift
+ // In addition, to support older non-multiplexed clients using the same concrete protocol
+ // the multiplexed processor is taught to use "ThriftTest" if the incoming request has no
+ // multiplexed call name decoration.
+ internal class SecondHandler : SecondService {
+ @Throws(TException::class)
+ override suspend fun secondtestString(thing: String): String {
+ return "testString(\"$thing\")"
+ }
+ }
+
+ internal class TestServerContext(var connectionId: Int) : ServerContext {
+
+ override fun <T> unwrap(iface: Class<T>): T {
+ try {
+ return if (isWrapperFor(iface)) {
+ iface.cast(this)
+ } else {
+ throw RuntimeException("The context is not a wrapper for " + iface.name)
+ }
+ } catch (e: Exception) {
+ throw RuntimeException(
+ "The context is not a wrapper and does not implement the interface"
+ )
+ }
+ }
+
+ override fun isWrapperFor(iface: Class<*>): Boolean {
+ return iface.isInstance(this)
+ }
+ }
+
+ internal class TestServerEventHandler() : TServerEventHandler {
+ private var nextConnectionId = 1
+ override fun preServe() {
+ println(
+ "TServerEventHandler.preServe - called only once before server starts accepting connections"
+ )
+ }
+
+ override fun createContext(input: TProtocol, output: TProtocol): ServerContext {
+ // we can create some connection level data which is stored while connection is alive &
+ // served
+ val ctx = TestServerContext(nextConnectionId++)
+ println(
+ "TServerEventHandler.createContext - connection #" +
+ ctx.connectionId +
+ " established"
+ )
+ return ctx
+ }
+
+ override fun deleteContext(
+ serverContext: ServerContext,
+ input: TProtocol,
+ output: TProtocol
+ ) {
+ val ctx = serverContext.unwrap(TestServerContext::class.java)
+ println(
+ "TServerEventHandler.deleteContext - connection #" +
+ ctx.connectionId +
+ " terminated"
+ )
+ }
+
+ override fun processContext(
+ serverContext: ServerContext,
+ inputTransport: TTransport,
+ outputTransport: TTransport
+ ) {
+ val ctx = serverContext.unwrap(TestServerContext::class.java)
+ println(
+ "TServerEventHandler.processContext - connection #" +
+ ctx.connectionId +
+ " is ready to process next request"
+ )
+ }
+ }
+}
+
+fun main(args: Array<String>) {
+ try {
+ var port = 9090
+ var ssl = false
+ var zlib = false
+ var transportType = "buffered"
+ var protocolType = "binary"
+// var serverType = "thread-pool"
+ var serverType = "nonblocking"
+ val domainSocket = ""
+ var stringLimit: Long = -1
+ var containerLimit: Long = -1
+ try {
+ for (i in args.indices) {
+ if (args[i].startsWith("--port")) {
+ port = Integer.valueOf(args[i].split("=").toTypedArray()[1])
+ } else if (args[i].startsWith("--server-type")) {
+ serverType = args[i].split("=").toTypedArray()[1]
+ serverType.trim { it <= ' ' }
+ } else if (args[i].startsWith("--port")) {
+ port = args[i].split("=").toTypedArray()[1].toInt()
+ } else if (args[i].startsWith("--protocol")) {
+ protocolType = args[i].split("=").toTypedArray()[1]
+ protocolType.trim { it <= ' ' }
+ } else if (args[i].startsWith("--transport")) {
+ transportType = args[i].split("=").toTypedArray()[1]
+ transportType.trim { it <= ' ' }
+ } else if (args[i] == "--ssl") {
+ ssl = true
+ } else if (args[i] == "--zlib") {
+ zlib = true
+ } else if (args[i].startsWith("--string-limit")) {
+ stringLimit = args[i].split("=").toTypedArray()[1].toLong()
+ } else if (args[i].startsWith("--container-limit")) {
+ containerLimit = args[i].split("=").toTypedArray()[1].toLong()
+ } else if (args[i] == "--help") {
+ println("Allowed options:")
+ println(" --help\t\t\tProduce help message")
+ println(" --port=arg (=$port)\tPort number to connect")
+ println(
+ " --transport=arg (=$transportType)\n\t\t\t\tTransport: buffered, framed, fastframed, zlib"
+ )
+ println(
+ " --protocol=arg (=$protocolType)\tProtocol: binary, compact, json, multi, multic, multij"
+ )
+ println(" --ssl\t\t\tEncrypted Transport using SSL")
+ println(" --zlib\t\t\tCompressed Transport using Zlib")
+ println(
+ " --server-type=arg (=$serverType)\n\t\t\t\tType of server: simple, thread-pool, nonblocking, threaded-selector"
+ )
+ println(" --string-limit=arg (=$stringLimit)\tString read length limit")
+ println(
+ " --container-limit=arg (=$containerLimit)\tContainer read length limit"
+ )
+ exitProcess(0)
+ }
+ }
+ } catch (e: Exception) {
+ System.err.println("Can not parse arguments! See --help")
+ exitProcess(1)
+ }
+ try {
+ when (serverType) {
+ "simple" -> {}
+ "thread-pool" -> {}
+ "nonblocking" -> {
+ if (ssl) {
+ throw Exception("SSL is not supported over nonblocking servers!")
+ }
+ }
+ "threaded-selector" -> {
+ if (ssl) {
+ throw Exception("SSL is not supported over nonblocking servers!")
+ }
+ }
+ else -> {
+ throw Exception("Unknown server type! $serverType")
+ }
+ }
+ when (protocolType) {
+ "binary" -> {}
+ "compact" -> {}
+ "json" -> {}
+ "multi" -> {}
+ "multic" -> {}
+ "multij" -> {}
+ else -> {
+ throw Exception("Unknown protocol type! $protocolType")
+ }
+ }
+ when (transportType) {
+ "buffered" -> {}
+ "framed" -> {}
+ "fastframed" -> {}
+ "zlib" -> {}
+ else -> {
+ throw Exception("Unknown transport type! $transportType")
+ }
+ }
+ } catch (e: Exception) {
+ System.err.println("Error: " + e.message)
+ exitProcess(1)
+ }
+
+ // Processors
+ val testHandler = TestHandler()
+ val testProcessor = ThriftTestProcessor(testHandler, scope = GlobalScope)
+ val secondHandler = TestServer.SecondHandler()
+ val secondProcessor = SecondServiceProcessor(secondHandler, scope = GlobalScope)
+
+ // Protocol factory
+ val tProtocolFactory: TProtocolFactory =
+ when (protocolType) {
+ "json", "multij" -> {
+ TJSONProtocol.Factory()
+ }
+ "compact", "multic" -> {
+ TCompactProtocol.Factory(stringLimit, containerLimit)
+ }
+ else -> { // also covers multi
+ TBinaryProtocol.Factory(stringLimit, containerLimit)
+ }
+ }
+ val tTransportFactory: TTransportFactory =
+ when (transportType) {
+ "framed" -> {
+ TFramedTransport.Factory()
+ }
+ "fastframed" -> {
+ TFastFramedTransport.Factory()
+ }
+ "zlib" -> {
+ TZlibTransport.Factory()
+ }
+ else -> { // .equals("buffered") => default value
+ TTransportFactory()
+ }
+ }
+ val serverEngine: TServer
+ // If we are multiplexing services in one server...
+ val multiplexedProcessor = TMultiplexedProcessor()
+ multiplexedProcessor.registerDefault(testProcessor)
+ multiplexedProcessor.registerProcessor("ThriftTest", testProcessor)
+ multiplexedProcessor.registerProcessor("SecondService", secondProcessor)
+ if (serverType == "nonblocking" || serverType == "threaded-selector") {
+ // Nonblocking servers
+ val tNonblockingServerSocket =
+ TNonblockingServerSocket(NonblockingAbstractServerSocketArgs().port(port))
+ if (serverType.contains("nonblocking")) {
+ // Nonblocking Server
+ val tNonblockingServerArgs = TNonblockingServer.Args(tNonblockingServerSocket)
+ tNonblockingServerArgs.processor(
+ if (protocolType.startsWith("multi")) multiplexedProcessor else testProcessor
+ )
+ tNonblockingServerArgs.protocolFactory(tProtocolFactory)
+ tNonblockingServerArgs.transportFactory(tTransportFactory)
+ serverEngine = TNonblockingServer(tNonblockingServerArgs)
+ } else { // server_type.equals("threaded-selector")
+ // ThreadedSelector Server
+ val tThreadedSelectorServerArgs =
+ TThreadedSelectorServer.Args(tNonblockingServerSocket)
+ tThreadedSelectorServerArgs.processor(
+ if (protocolType.startsWith("multi")) multiplexedProcessor else testProcessor
+ )
+ tThreadedSelectorServerArgs.protocolFactory(tProtocolFactory)
+ tThreadedSelectorServerArgs.transportFactory(tTransportFactory)
+ serverEngine = TThreadedSelectorServer(tThreadedSelectorServerArgs)
+ }
+ } else {
+ // Blocking servers
+
+ // SSL socket
+ val tServerSocket: TServerSocket = if (ssl) {
+ TSSLTransportFactory.getServerSocket(port, 0)
+ } else {
+ TServerSocket(ServerSocketTransportArgs().port(port))
+ }
+ if (serverType == "simple") {
+ // Simple Server
+ val tServerArgs = TServer.Args(tServerSocket)
+ tServerArgs.processor(
+ if (protocolType.startsWith("multi")) multiplexedProcessor else testProcessor
+ )
+ tServerArgs.protocolFactory(tProtocolFactory)
+ tServerArgs.transportFactory(tTransportFactory)
+ serverEngine = TSimpleServer(tServerArgs)
+ } else { // server_type.equals("threadpool")
+ // ThreadPool Server
+ val tThreadPoolServerArgs = TThreadPoolServer.Args(tServerSocket)
+ tThreadPoolServerArgs.processor(
+ if (protocolType.startsWith("multi")) multiplexedProcessor else testProcessor
+ )
+ tThreadPoolServerArgs.protocolFactory(tProtocolFactory)
+ tThreadPoolServerArgs.transportFactory(tTransportFactory)
+ serverEngine = TThreadPoolServer(tThreadPoolServerArgs)
+ }
+ }
+
+ // Set server event handler
+ serverEngine.setServerEventHandler(TestServer.TestServerEventHandler())
+
+ // Run it
+ println(
+ "Starting the " +
+ (if (ssl) "ssl server" else "server") +
+ " [" +
+ protocolType +
+ "/" +
+ transportType +
+ "/" +
+ serverType +
+ "] on " +
+ if (domainSocket === "") "port $port" else "unix socket $domainSocket"
+ )
+ serverEngine.serve()
+ } catch (x: Exception) {
+ x.printStackTrace()
+ }
+ println("done.")
+}
diff --git a/lib/kotlin/cross-test-server/src/main/resources/logback.xml b/lib/kotlin/cross-test-server/src/main/resources/logback.xml
new file mode 100644
index 0000000..98309d7
--- /dev/null
+++ b/lib/kotlin/cross-test-server/src/main/resources/logback.xml
@@ -0,0 +1,32 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you under the Apache License, Version 2.0 (the
+ ~ "License"); you may not use this file except in compliance
+ ~ with the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing,
+ ~ software distributed under the License is distributed on an
+ ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ ~ KIND, either express or implied. See the License for the
+ ~ specific language governing permissions and limitations
+ ~ under the License.
+ -->
+
+<configuration>
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <!-- encoders are assigned the type
+ ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
+ <encoder>
+ <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <root level="debug">
+ <appender-ref ref="STDOUT"/>
+ </root>
+</configuration>