Sunday, May 9, 2021

Ktor Kotlin Backend Development with MySql Database Demo Project - Mobile Development

Kotlin-Ktor-MySql-Webservice Development

GitHub Full Source Code:
https://github.com/Abhinaw/nearbyapi_abhi/tree/master

I was getting demand for Ktor Kotlin Backend Development with Sample MySql Database Code So in this blog I will give you all full access to the source code on my Github URL.

SQL file is also committed to the parent folder and you can easily import and configure the project.

Why Ktor?

Use what you need. Ktor allows you to transparently configure only the functionality your project requires. No magic involved!

Extensible

Extend what you need. With a configurable pipeline, you can create the extensions you need and place them anywhere you want.

Multiplatform

Run it where you need it. Built from the ground up with Kotlin Multiplatform technology, you can deploy Ktor applications anywhere.

Asynchronous

Scales as you need it. Using Kotlin coroutines, Ktor is truly asynchronous and highly scalable. Use the power of non-blocking development without the callback nightmare.

Java Threads Vs Kotlin Coroutines 

I myself as a programmer/developer I tested it by writing code that's what I do. I read somewhere that Courotines is much better than Java threads I did not believe it initially but to test it I did one thing like I have an 8GB RAM Multi-core Windows operating system. 

To test it, I wrote a simple Java and Kotlin Program where what i did is like created 100 threads from Fork/Join Pool of threads and the same concept with Kotlin Coroutines and the result was amazing to me. 

In the case of Java, my system got crashed and it consumes my all resources when I checked my task manager it was consuming 100% of my system but in the case of Coroutines with the same program, it only consumed my 80% resource and that is an amazing part to me and even the program did not crash. this makes me a little uncomfortable worked on Java for more than 10 years till now and maybe it proves how powerful Kotlin is. As a developer/programmer I believe it because I checked it by doing my hands dirty with it.

Thoughts for Food for Android Developers:

I strongly believe that it will take time to improve like Spring Eco-System but I find it very useful in terms of Mobile Application Development because in every mobile app we need middle-tier for data transaction and there it saves like earlier for that Mobile Developer can not cater that requirement but now they can easily cater the same because they can write the required code for Middle-Tier certainly Android Developers.

And there is one more advantage that I can see is like you can create a single project structure to care both the platforms I mean Front-End and Middle-Tier, Yes there can be an architectural difference but indeed very help full when it comes to CI/CD.

Ktor is a framework to easily build connected applications – web applications, HTTP services, mobile and browser applications. Modern connected applications need to be asynchronous to provide the best experience to users, and Kotlin coroutines provide awesome facilities to do it in an easy and straightforward way.

While not yet entirely there, the goal of Ktor is to provide an end-to-end multiplatform application framework for connected applications. Currently, JVM client and server scenarios are supported, as well as JavaScript, iOS, and Android clients, and we are working on bringing server facilities to native environments, and client facilities to other native targets.

When you create a project in IntelliJ IDE:

Name: Specify a project name.

Location: Specify a directory for your project.

Build System: Choose the desired build system. This can be Gradle with Kotlin or Groovy DSL or Maven.

Website: Specify a domain used to generate a package name.

Artifact: This field shows a generated artifact name.

Ktor Version: Choose the required Ktor version.

Engine: Select an engine used to run a server.

Configuration in: Choose whether to specify server parameters in code or in a HOCON file.

On the next page, you can choose a set of features- building blocks that provide common functionality of a Ktor application, for example, authentication, serialization, and content-encoding, compression, cookie support, and so on.

application.conf:

ktor {

    deployment {

        port = 8080

    }

    application {

        modules = [ com.jetbrains.handson.chat.server.ApplicationKt.module ]

    }

}

Application.Kt:

package com.abhinearby

import com.fasterxml.jackson.databind.SerializationFeature

import io.ktor.application.*

import io.ktor.features.*

import io.ktor.gson.*

import io.ktor.http.*

import io.ktor.http.cio.websocket.*

import io.ktor.jackson.*

import io.ktor.request.*

import io.ktor.response.*

import io.ktor.routing.*

import io.ktor.websocket.*

import org.slf4j.event.Level

import java.util.*

import kotlin.collections.LinkedHashSet

fun main(args: Array<String>): Unit = io.ktor.server.netty.EngineMain.main(args)

@Suppress("unused") // Referenced in application.conf

@kotlin.jvm.JvmOverloads

fun Application.module(testing: Boolean = false) {

    install(WebSockets)

    install(CallLogging) {

        level = Level.INFO

        filter { call -> call.request.path().startsWith("/") }

    }

    install(ContentNegotiation) {

        gson {

            setPrettyPrinting()

        }

        jackson {

            enable(SerializationFeature.INDENT_OUTPUT)

        }

    }

 routing {

        /* For only Testing*/

        get("/json/jackson") {

            call.respond(mapOf("hello" to "world"))

        }

        /********************Near By App Start*********************/

        val repository: RegistrationRepository = MySqlRegistrationToDoRepository()

        post("/registration")

        {

            val todoDraft = call.receive<RegistrationDraft>()

            val todo = repository.addToRegistrationTable(todoDraft)

            call.respond(todo)

        }

        get("/getallregistrationdata")

        {

            call.respond(repository.getAllRegisteredTableData())

        }

        put("/registration/{id}")

        {

            val todoDraft = call.receive<RegistrationDraft>()

            val todoId = call.parameters["id"]?.toIntOrNull()

            if (todoId == null) {

                call.respond(HttpStatusCode.BadRequest, "id  parameter has to be a number!")

                return@put

            }

            val update = repository.updateToRegistrationTable(todoId, todoDraft)

            if (update) {

                call.respond(HttpStatusCode.OK)

            } else {

                call.respond(HttpStatusCode.NotFound, "registered user with the id $todoId not found")

            }

        }

        /**************Login********************/

        val loginRepo: LoginRepository = MySqlLoginToDoRepository()

        post("/login")

        {

            val todoDraft = call.receive<LoginDraft>()

            val todo = loginRepo.validateToLoginTable(todoDraft)

            call.respond(todo)

        }

        get("/getlogindata")

        {

            val repository: LoginRepository = MySqlLoginToDoRepository()

            call.respond(repository.getAllLoginTableData())

        }

        ///////////////////////////Websocket Chat System////////////////////////////

        val connections = Collections.synchronizedSet<Connection?>(LinkedHashSet())

        webSocket("/chat") {

            println("Adding user!")

            val thisConnection = Connection(this)

            connections += thisConnection

            try {

                send("You are connected! There are ${connections.count()} users here.")

                for (frame in incoming) {

                    frame as? Frame.Text ?: continue

                    val receivedText = frame.readText()

                    val textWithUsername = "[${thisConnection.name}]: $receivedText"

                    connections.forEach {

                        it.session.send(textWithUsername)

                    }

                }

            } catch (e: Exception) {

                println(e.localizedMessage)

            } finally {

                println("Removing $thisConnection!")

                connections -= thisConnection

            }

        }

        /////////////////////////////////////Chat////////////////////////////

    }

}

DatanbaseManager.Kt:


package com.abhinearby.database

import org.ktorm.database.Database

import org.ktorm.dsl.eq

import org.ktorm.dsl.insertAndGenerateKey

import org.ktorm.dsl.update

import org.ktorm.entity.sequenceOf

import org.ktorm.entity.toList

import java.sql.Connection

class DatabaseManager {

private val hostname = "127.0.0.1"

    private val databaseName = "nearby_abhi"

    private val username = "root"

    private val password = ""

    private val ktormDatabase: Database

    init {

        val jdbcUrl = "jdbc:mysql://$hostname:3306/$databaseName?user=$username&password=$password&useSSL=false"

        ktormDatabase = Database.connect(jdbcUrl)

    }

    /************Registration for the App*************/

    fun addRegistrationDataToDB(draft: RegistrationDraft): Registration {

        val insertedId = ktormDatabase.insertAndGenerateKey(DBRegistrationTable)

        {

            set(DBRegistrationTable.firstname, draft.firstname)

            set(DBRegistrationTable.lastname, draft.lastname)

            set(DBRegistrationTable.phoneno, draft.phoneno)

            set(DBRegistrationTable.address, draft.address)

            set(DBRegistrationTable.photo, draft.photo)

            set(DBRegistrationTable.username, draft.username)

            set(DBRegistrationTable.password, draft.password)

        } as Int

        val registration = Registration(

            insertedId,

            draft.firstname,

            draft.lastname,

            draft.phoneno,

            draft.address,

            draft.photo,

            draft.username,

            draft.password

        )

        insertRow(registration)

        return registration

    }

    private fun insertRow(registration: Registration) {

        val id = registration.id

        val username = registration.username

        val password = registration.password

        val fid = registration.id

        val sql =

            "INSERT INTO `login`(`id`, `username`, `password`, `fid`) VALUES ('$id','$username','$password','$fid')"

        ktormDatabase.useConnection { connection: Connection ->

            with(connection) {

                createStatement().execute(sql)

            }

            println("inserted!!")

        }

    }

    fun updateRegistrationDataToDB(id: Int, draft: RegistrationDraft): Boolean {

        val updatedRows = ktormDatabase.update(DBRegistrationTable)

        {

            set(DBRegistrationTable.firstname, draft.firstname)

            set(DBRegistrationTable.lastname, draft.lastname)

            set(DBRegistrationTable.phoneno, draft.phoneno)

            set(DBRegistrationTable.address, draft.address)

            set(DBRegistrationTable.photo, draft.photo)

            set(DBRegistrationTable.username, draft.username)

            set(DBRegistrationTable.password, draft.password)

            where {

                it.id eq id

            }

        }

        return updatedRows > 0

    }

    fun getAllRegisteredTableData(): List<DBNearByRegistrationEntity> {

        return ktormDatabase.sequenceOf(DBRegistrationTable).toList()

    }

    /**************************Registration End*************************************/

    /************************Login **************************/

    fun validateLogin(draft: LoginDraft): LoginDraft {

        val table = "login"

        val sql = "SELECT * FROM $table"

        ktormDatabase.useConnection { connection: Connection ->

            val rs = connection.createStatement().executeQuery(sql)

            while (rs.next()) {

                println(

                    "id: ${rs.getInt("id")}\t" +

                            "username: $${rs.getString("username")}\t" +

                            "password: ${rs.getString("password")}\t" +

                            "fid: $${rs.getDouble("fid")}"

                )

                val userName = rs.getString("username")

                val pass = rs.getString("password")

                if (draft.username.equals(userName)

                    && draft.password.equals(pass)

                ) {

                    break

                }

            }

        }

        return draft!!

    }

    fun getAllLoginTableData(): List<DBNearByLoginEntity> {

        return ktormDatabase.sequenceOf(DBLoginTable).toList()

    }

}

Entity.kt:

import org.ktorm.entity.Entity

import org.ktorm.schema.Table

import org.ktorm.schema.int

import org.ktorm.schema.varchar

object DBRegistrationTable : Table<DBNearByRegistrationEntity>("dbregistrationtable") {

    val id = int("id").primaryKey().bindTo { it.id }

    val firstname = varchar("firstname").bindTo { it.firstname }

    val lastname = varchar("lastname").bindTo { it.lastname }

    var phoneno = varchar("phoneno").bindTo { it.phoneno }

    var address = varchar("address").bindTo { it.address }

    var photo = varchar("photo").bindTo { it.photo }

    var username = varchar("username").bindTo { it.username }

    var password = varchar("password").bindTo { it.password }

}

interface DBNearByRegistrationEntity : Entity<DBNearByRegistrationEntity> {

    companion object : Entity.Factory<DBNearByRegistrationEntity>()

    val id: Int

    val firstname: String

    val lastname: String

    val phoneno: String

    val address: String

    val photo: String

    val username : String

    val password : String

}

RegistrationRepository.kt:

import com.abhinearby.entities.registration.Registration

import com.abhinearby.entities.registration.RegistrationDraft

interface RegistrationRepository {

    fun addToRegistrationTable(draft: RegistrationDraft): Registration

    fun updateToRegistrationTable(id: Int, draft: RegistrationDraft): Boolean

    fun getAllRegisteredTableData() : List<Registration>

}

PostMan Webservcie Request-Response Example:

1)http://localhost:8081/getallregistrationdata

[
    {
        "id"29,
        "firstname""abhi",
        "lastname""tripathi",
        "phoneno""8130752107",
        "address""siddha happyville",
        "photo""",
        "username""abhi",
        "password""abhi"
    },
    {
        "id"30,
        "firstname""abhinawtripathi34@gmail.com",
        "lastname""tripathi",
        "phoneno""8130752107",
        "address""cyfydufffi",
        "photo""",
        "username""test",
        "password""test"
    },
    {
        "id"31,
        "firstname""abhinawtripathi34@gmail.com",
        "lastname""tripathi",
        "phoneno""8130752107",
        "address""cyfydufffi",
        "photo""",
        "username""test",
        "password""test"
    },
    {
        "id"32,
        "firstname""buvu",
        "lastname""cyxx",
        "phoneno""753577789",
        "address""vyuvvjvj",
        "photo""",
        "username""test",
        "password""test"
    },
    {
        "id"33,
        "firstname""g7g7",
        "lastname""cyyc",
        "phoneno""8765677999",
        "address""gyuj",
        "photo""",
        "username""tests107@mailsac.com",
        "password""test"
    },
    {
        "id"34,
        "firstname""g7g7",
        "lastname""cyyc",
        "phoneno""8765677999",
        "address""gyuj",
        "photo""",
        "username""tests107@mailsac.com",
        "password""test"
    },
    {
        "id"35,
        "firstname""g7g7",
        "lastname""cyyc",
        "phoneno""8765677999",
        "address""gyuj",
        "photo""",
        "username""tests107@mailsac.com",
        "password""test"
    },
    {
        "id"36,
        "firstname""g7g7",
        "lastname""cyyc",
        "phoneno""8765677999",
        "address""gyuj",
        "photo""",
        "username""tests107@mailsac.com",
        "password""test"
    },
    {
        "id"37,
        "firstname""test",
        "lastname""test",
        "phoneno""875678887",
        "address""hhjbjhhj",
        "photo""",
        "username""test",
        "password""test"
    },
    {
        "id"38,
        "firstname""test",
        "lastname""test",
        "phoneno""875678887",
        "address""hhjbjhhj",
        "photo""",
        "username""test",
        "password""test"
    },
    {
        "id"39,
        "firstname""test",
        "lastname""test",
        "phoneno""875678887",
        "address""hhjbjhhj",
        "photo""",
        "username""test",
        "password""test"
    },
    {
        "id"40,
        "firstname""",
        "lastname""",
        "phoneno""",
        "address""",
        "photo""",
        "username""",
        "password"""
    },
    {
        "id"41,
        "firstname""",
        "lastname""",
        "phoneno""",
        "address""",
        "photo""",
        "username""",
        "password"""
    },
    {
        "id"42,
        "firstname""",
        "lastname""",
        "phoneno""",
        "address""",
        "photo""",
        "username""",
        "password"""
    },
    {
        "id"43,
        "firstname""",
        "lastname""",
        "phoneno""",
        "address""",
        "photo""",
        "username""",
        "password"""
    },
    {
        "id"44,
        "firstname""",
        "lastname""",
        "phoneno""",
        "address""",
        "photo""",
        "username""",
        "password"""
    },
    {
        "id"45,
        "firstname""",
        "lastname""",
        "phoneno""",
        "address""",
        "photo""",
        "username""",
        "password"""
    },
    {
        "id"46,
        "firstname""abhi",
        "lastname""sbhi",
        "phoneno""6858845",
        "address""ifdjdffuff kgufpfug bufstzcufuc kvfuk. kuddhjcfucucyddcpjvucyf",
        "photo""",
        "username""abhi",
        "password""abhi"
    }
]

2)http://localhost:8081/registration/29 (PUT Servcie)

request:

{
        "id"29,
        "firstname""abhinaw",
        "lastname""tri",
        "phoneno""8130752107",
        "address""howrah",
        "photo""/abc.png",
        "username""abhinaw",
        "password""abhinaw"
    }
Response:200OK
Time:251 ms
Size:62 B Save Response





























3)http://localhost:8081/registration (POST Service)









Websocket Communication System:

///////////////////////////Chat////////////////////////////
val connections = Collections.synchronizedSet<Connection?>(LinkedHashSet())
webSocket("/chat") {
println("Adding user!")
val thisConnection = Connection(this)
connections += thisConnection
try {
send("You are connected! There are ${connections.count()} users here.")
for (frame in incoming) {
frame as? Frame.Text ?: continue
val receivedText = frame.readText()
val textWithUsername = "[${thisConnection.name}]: $receivedText"
connections.forEach {
it.session.send(textWithUsername)
}
}
} catch (e: Exception) {
println(e.localizedMessage)
} finally {
println("Removing $thisConnection!")
connections -= thisConnection
}
}
/////////////////////////////////////Chat////////////////////////////

Very easy to communicate and very simple code for example.

Output Window:





1 comment:

Anonymous said...


Thank you very much, Sir, It really helps as very new and seems very few organization started working on it.