KtorでおうちAPIをリプレースした話

Bbf2a0e77c98104ac16ef6d0098c1385?s=47 yagi2
December 18, 2017

 KtorでおうちAPIをリプレースした話

2017/12/18 CA.kt #5 @大阪 LT

Bbf2a0e77c98104ac16ef6d0098c1385?s=128

yagi2

December 18, 2017
Tweet

Transcript

  1. torͰ͓͏ͪAPI
 ɹΛϦϓϨʔεͨ͠࿩ 2017/12/18 CA.kt #5 @େࡕ Itsuki AOYAGI

  2. ͍͋ͭ͝͞ - ࣗݾ঺հ - • ໊લ : ੨༄ थ Itsuki

    AOYAGIʢ23ʣ
 a.k.a ΍͗ʹ͍ • AndroidΞϓϦΤϯδχΞ • Kotlinྺ : ໿൒೥ • Twitter : @yaginier • GitHub etc… : @yagi2 / @yagi21 • ͓͏͍͖ͪͩ͢
  3. ͍͋ͭ͝͞ - ࣗݾ঺հ - • ໊લ : ੨༄ थ Itsuki

    AOYAGIʢ23ʣ
 a.k.a ΍͗ʹ͍ • AndroidΞϓϦΤϯδχΞ • Kotlinྺ : ໿൒೥ • Twitter : @yaginier • GitHub etc… : @yagi2 / @yagi21 • ͓͏͍͖ͪͩ͢
  4. ͓͏ͪϋοΫͯ͠·͔͢ʁ

  5. ͓͏ͪϋοΫ ͓͏ͪΛΑΓշదͳ΋ͷʹ͢ΔͨΊʹ ΨδΣοτ΍αʔϏεΛ૊Έ߹Θͤͯ ϋοϐʔʹ͢ΔͨΊͷ͋Ε͜Ε ɻ ࠷ۙͩͱεϚʔτεϐʔΧʔ͕͍ͬͺ͍͋ΔͷͰ ͦΕΒܦ༝ͰՈిͱ͔Λૢ࡞Ͱ͖Δɻ

  6. ͓͏ͪϋοΫ - զ͕Ո - ɾRaspberry Piʢॳ୅ʣ ɹˠ ͓͏ͪAPI͕͸͍͑ͯΔʢPHPʣ ɹɹˠ ϒϥ΢β͔ΒHTTPSܦ༝ͯ͠ϖʔδ͔Βୟ͘

    ɹɹˠ ࣗ࡞ͷAndroidΞϓϦ͔ΒϦΫΤετ Ұྫ ɾࣗ୐ͷσεΫτοϓPCͷిݯΛ֎͔ΒೖΕΔ ɾΤΞίϯɺςϨϏ౳ͷ੺֎ઢͰૢ࡞͢ΔՈిͷૢ࡞ ɾϥδΦʢRasPiΛrtmpετϦʔϛϯά࢖ͬͯϥδΦʹ͍ͯͨ͠ʣ
  7. ͓͏ͪϋοΫ - զ͕Ո - ɾRaspberry Piʢॳ୅ʣ ɹˠ ͓͏ͪAPI͕͸͍͑ͯΔʢPHPʣ ɹɹˠ ϒϥ΢β͔ΒHTTPSܦ༝ͯ͠ϖʔδ͔Βୟ͘

    ɹɹˠ ࣗ࡞ͷAndroidΞϓϦ͔ΒϦΫΤετ Ұྫ ɾࣗ୐ͷσεΫτοϓPCͷిݯΛ֎͔ΒೖΕΔ ɾΤΞίϯɺςϨϏ౳ͷ੺֎ઢͰૢ࡞͢ΔՈిͷૢ࡞ ɾϥδΦʢRasPiΛrtmpετϦʔϛϯά࢖ͬͯϥδΦʹ͍ͯͨ͠ʣ
  8. ͓͏ͪϋοΫ - զ͕Ո - ɾRaspberry Piʢॳ୅ʣ ɹˠ ͓͏ͪAPI͕͸͍͑ͯΔʢPHPʣ ɹɹˠ ϒϥ΢β͔ΒHTTPSܦ༝ͯ͠ϖʔδ͔Βୟ͘

    ɹɹˠ ࣗ࡞ͷAndroidΞϓϦ͔ΒϦΫΤετ Ұྫ ɾࣗ୐ͷσεΫτοϓPCͷిݯΛ֎͔ΒೖΕΔ ɾΤΞίϯɺςϨϏ౳ͷ੺֎ઢͰૢ࡞͢ΔՈిͷૢ࡞ ɾϥδΦʢRasPiΛrtmpετϦʔϛϯά࢖ͬͯϥδΦʹ͍ͯͨ͠ʣ ͍ͭ͜ΛKotlinʹॻ͖׵͑Δ
  9. αʔόʔαΠυ Kotlin

  10. αʔόʔαΠυKotlin ɾSpring Boot ɹҎલKotlin + Spring BootͰJSONΛు͘APIΛ࡞ͬͨ͜ͱ͕͋Δ ɹ ͦͷ࣌ͷϒϩά →

    KotlinͱSpring BootΛ࢖ͬͯαΫοͱୟ͚ΔAPIΛ࡞Δ ɾKotr ɹΊͬͪΌബ͍ ɹ ɹKtor is a framework for quickly creating web applications in Kotlin with minimal effort.
  11. Ktor fun main(args: Array<String>) { embeddedServer(Netty, 8080) { routing {

    get("/") { call.respondText("Hello, world!", ContentType.Text.Html) } } }.start(wait = true) } ɾബ͗ͯ͢README͕୹͍ ɹɹɹɹউखͳΠϝʔδ ɹɹɹɹɹɹɹSpringBoot → Ruby on Rails ɹɹɹɹɹɹɹKtor → Sinatra
  12. Ktor - WoL API - KtorΛ࢖ͬͯɺࣗ୐PCͷిݯΛ֎͔ΒೖΕΔAPIΛॻ͖௚͢ ݱࡏͷ࢓༷ ɾΫϥΠΞϯτ͸POSTͰର৅ͷMacΞυϨεΛ౤͛Δ ɾαʔόʔ͸ड͚औͬͨΒͦͷMacΞυϨεʹରͯ͠WoL͢Δ ɹʢexecͰwakeonlanίϚϯυΛୟ͍͍ͯΔʣ

  13. Ktor - WoL API - KtorΛ࢖ͬͯɺࣗ୐PCͷిݯΛ֎͔ΒೖΕΔAPIΛॻ͖௚͢ ৽͍͠࢓༷ ɾΫϥΠΞϯτ͸POSTͰର৅ͷϗετ໊Λ౤͛Δ ɹˠ ϗετ໊΍ͦΕʹ४ͣΔ΋ͷʢཧ༝͸ޙड़ʣΛ౤͛Δ

    ɹˠ DB౳αʔόʔଆͰϗετ໊ͱMacΞυϨεΛ͓͍࣋ͬͯͯࢀর ɾαʔόʔ͸ड͚औͬͨΒର৅ʹରͯ͠WoL͢Δ ɹ͔ͤͬ͘ͳͷͰϚδοΫύέοτૹ৴Λ࣮૷͢Δ
  14. Ktor - WoL API - install(Routing) { post("/wol") { val

    name = call.receiveParameters()[“name”] val host = ϗετΛDB͔Βݕࡧ(name) if (host == null) { call.respond("ݟ͔ͭΒͳ͔ͬͨ") } else { try { WakeOnLan(host.macAddr).sendMagicPacket() call.respond("ૹͬͨ") } catch (e: IOException) { call.respond("ૹΔͷʹࣦഊͨ͠") } } } ɾPOSTͰड͚औͬͯϚδοΫύέοτΛૹΔ ɹˠ Ͳͷϗετʹରͯ͠ϚδοΫύέοτΛૹΔͷ͔͸ύϥϝʔλͰ ɹˠ DBʹ͸Exposed + MySQLΛ࢖༻
  15. Ktor - WoL API - install(Routing) { post("/wol") { val

    name = call.receiveParameters()[“name”] val host = ϗετΛDB͔Βݕࡧ(name) if (host == null) { call.respond("ݟ͔ͭΒͳ͔ͬͨ") } else { try { WakeOnLan(host.macAddr).sendMagicPacket() call.respond("ૹͬͨ") } catch (e: IOException) { call.respond("ૹΔͷʹࣦഊͨ͠") } } } ɾPOSTͰड͚औͬͯϚδοΫύέοτΛૹΔ ɹˠ Ͳͷϗετʹରͯ͠ϚδοΫύέοτΛૹΔͷ͔͸ύϥϝʔλͰ ɹˠ DBʹ͸Exposed + MySQLΛ࢖༻
  16. Ktor - WoL API - install(Routing) { post("/wol") { val

    name = call.receiveParameters()[“name”] val host = ϗετΛDB͔Βݕࡧ(name) if (host == null) { call.respond("ݟ͔ͭΒͳ͔ͬͨ") } else { try { WakeOnLan(host.macAddr).sendMagicPacket() call.respond("ૹͬͨ") } catch (e: IOException) { call.respond("ૹΔͷʹࣦഊͨ͠") } } } ɾPOSTͰड͚औͬͯϚδοΫύέοτΛૹΔ ɹˠ Ͳͷϗετʹରͯ͠ϚδοΫύέοτΛૹΔͷ͔͸ύϥϝʔλͰ ɹˠ DBʹ͸Exposed + MySQLΛ࢖༻ object Computers : IntIdTable() { val hostName = varchar("hostname", 50).index() val macAddr = varchar("mac_addr", 50) } class Computer(id: EntityID<Int>) : IntEntity(id) { companion object : IntEntityClass<Computer>(Computers) var hostname by Computers.hostName var macAddr by Computers.macAddr } fun findByHostName(hostName: String?): Computer? { return transaction { Computer.find { Computers.hostName eq hostName }.firstOrNull() } }
  17. Ktor - WoL API - install(Routing) { post("/wol") { val

    name = call.receiveParameters()[“name”] val host = ϗετΛDB͔Βݕࡧ(name) if (host == null) { call.respond("ݟ͔ͭΒͳ͔ͬͨ") } else { try { WakeOnLan(host.macAddr).sendMagicPacket() call.respond("ૹͬͨ") } catch (e: IOException) { call.respond("ૹΔͷʹࣦഊͨ͠") } } } ɾPOSTͰड͚औͬͯϚδοΫύέοτΛૹΔ ɹˠ Ͳͷϗετʹରͯ͠ϚδοΫύέοτΛૹΔͷ͔͸ύϥϝʔλͰ ɹˠ DBʹ͸Exposed + MySQLΛ࢖༻
  18. Ktor - WoL API - class WakeOnLan(private val macAddress: String)

    { @Throws(IOException::class) fun sendMagicPacket() { val iNetAddress = InetAddress.getByName("255.255.255.255") val magicPacket = getMagicPacket(macAddress) DatagramSocket().send(DatagramPacket(magicPacket, magicPacket.size, iNetAddress, 8080)) } private fun getMagicPacket(macAddress: String): ByteArray { val packet = ByteArray(102) var index = 0 repeat(6) { packet[index++] = 0xFF.toByte() } val macAddressByte = getMacAddressByte(macAddress) repeat(16) { repeat(6) { packet[index++] = macAddressByte[it] } } return packet } private fun getMacAddressByte(macAddress: String): ByteArray = macAddress.split(":").map { Integer.parseInt(it, 16).toByte() }.toByteArray() }
  19. ׬੒ʂ ͨ͠ͷͰɺGoogleHome͔Β࢖͑ΔΑ͏ʹ͢Δ

  20. Google Home + IFTTT Google HomeͷԻ੠ίϚϯυͰIFTTTͷτϦΨʔΛୟ͍ͯ IFTTT͸WebHookͰ͓ՈAPIʹPOST͢Δ

  21. Google Home + IFTTT ໰୊఺ ී௨ʹϓϩάϥϜ͔ΒPOST͢Δͱ͖͸ $ curl --data-urlencode "name=hoge"

    http://api.example.com/wol Google Homeͩͱ”name=hoge”͕ͭΒ͍ ɹˠ ೔ຊޠͰͷΤΠϦΞεΛ෇͚ͯDBͷΧϥϜʹೖΕ͓ͯ͘ ྫʣʮͶ͐ɺGoogle ͏ͪͷ͜ͱΓΛىͯ͜͠ʯ ɹɹˠ name=͜ͱΓ ɹɹˠ DB͔Βʮ͜ͱΓʯͰҰக͢ΔͷΛݕࡧͯ͠΍Δ
  22. Google Home + IFTTT object Computers : IntIdTable() { val

    hostName = varchar("hostname", 50).index() val macAddr = varchar("mac_addr", 50) } class Computer(id: EntityID<Int>) : IntEntity(id) { companion object : IntEntityClass<Computer>(Computers) var hostname by Computers.hostName var macAddr by Computers.macAddr } fun findByHostName(hostName: String?): Computer? { return transaction { Computer.find { Computers.hostName eq hostName }.firstOrNull() } } object Computers : IntIdTable() { val hostName = varchar("hostname", 50).index() val name = varchar("name", 50) val macAddr = varchar("mac_addr", 50) } class Computer(id: EntityID<Int>) : IntEntity(id) { companion object : IntEntityClass<Computer>(Computers) var hostname by Computers.hostName var name by Computers.name var macAddr by Computers.macAddr } fun findByHostName(hostName: String?): Computer? { return transaction { Computer.find { Computers.hostName eq hostName }.firstOrNull() } } fun findByName(name: String?): Computer? { return transaction { Computer.find { Computers.name eq name }.firstOrNull() } }
  23. Google Home + IFTTT transaction { Computer.new { hostname =

    "kotori" name = "͜ͱΓ" macAddr = “FF:FF:FF:FF:FF:FF” } } ͓Ոʹ͋ΔϚγϯΛDBʹೖΕͯ͋͛Δɻ $ curl --data-urlencode "name=kotori" http://api.example.com:/wol ʮͶ͐ɺGoogle ͓͏ͪͷ͜ͱΓΛىͯ͜͠ʯ ✨྆ํڞOK✨

  24. Google Home + IFTTT ஫ҙ఺ ʮͶ͐ɺGoogle ͏ͪͷ͜ͻͳͨΛىͯ͜͠ʯ → ౉ͬͯ͘Δ໊લ͕ʮ͜ ͻͳ

    ͨʯ ʮͶ͐ɺGoogle ͏ͪͷͪΌͪΌΛىͯ͜͠ʯ → ౉ͬͯ͘Δ໊લ͕ʮ஡ʑʯ ղܾࡦ → 1ճࢼͯ͠Կ͕౉ͬͯ͘Δͷ͔ΛݟΔ
  25. ຊ೔ͷ͓͠ͳ͕͖ ɾAPI ɹˠ https://github.com/yagi2/Kaya ɾKtor ɹˠ https://github.com/ktorio/ktor ɾExposed ɹˠ https://github.com/JetBrains/Exposed

  26. Have a Nice Kotlin!