Mengapa saya menggunakan Scala / Lift over Java / Spring? [Tutup]

151

Saya tahu pertanyaan ini agak terbuka tetapi saya telah melihat Scala / Lift sebagai alternatif untuk Java / Spring dan saya bertanya-tanya apa saja keuntungan nyata yang dimiliki Scala / Lift. Dari perspektif dan pengalaman saya, Java Annotations and Spring benar-benar meminimalkan jumlah pengkodean yang harus Anda lakukan untuk suatu aplikasi. Apakah Scala / Lift meningkatkan hal itu?

Chris J
sumber
Pertanyaannya terlalu tua. Tetapi sekarang pertanyaannya adalah "Mengapa saya harus menggunakan Scala / Play over XYX" dan ada banyak alasan bagus. Saya pindah ke Play dan tidak pernah melihat ke belakang.
Jus12

Jawaban:

113

Mari kita asumsikan kita sama-sama nyaman di Scala dan Jawa, dan mengabaikan perbedaan bahasa (besar) kecuali yang berkaitan dengan Spring atau Lift.

Spring and Lift hampir secara diametris menentang dalam hal kedewasaan dan tujuan.

  • Musim semi sekitar lima tahun lebih tua dari Lift
  • Angkat bersifat monolitik dan hanya menargetkan web; Spring adalah modular dan menargetkan aplikasi web dan "biasa"
  • Spring mendukung sejumlah besar fitur Java EE; Angkat mengabaikan hal itu

Dalam sebuah kalimat, Spring adalah kelas berat dan Angkat adalah kelas ringan. Dengan tekad dan sumber daya yang memadai, Anda dapat mengubahnya, tetapi Anda akan membutuhkan banyak dari keduanya.

Berikut adalah perbedaan nyata yang melekat dalam pikiran saya setelah bekerja dengan kedua kerangka kerja. Ini bukan daftar lengkap, yang bagaimanapun tidak bisa saya kompilasi. Apa yang tampak paling menarik bagiku ...

  1. Lihat filosofi

    Lift mendorong penempatan beberapa materi tampilan dalam metode cuplikan / tindakan. Kode snippet akan ditaburi dengan elemen form, <div>s, <p>s, dll.

    Ini sangat kuat dan berguna, terutama karena Scala memiliki mode XML tingkat bahasa bawaan. Seseorang dapat menulis inline XML dalam metode Scala, termasuk binding variabel dalam kurung kurawal. Ini bisa menyenangkan untuk layanan XML yang sangat sederhana atau maket layanan - Anda dapat mengeluarkan serangkaian tindakan respons HTTP semua dalam satu file sangat bagus, tanpa templat atau banyak konfigurasi petugas. Kelemahannya adalah kompleksitas. Bergantung pada seberapa jauh Anda melangkah, ada pemisahan yang samar antara pandangan dan logika, atau tidak ada pemisahan.

    Sebaliknya, penggunaan Spring untuk webapp secara teratur memberlakukan pemisahan yang kuat antara tampilan dan yang lainnya. Saya pikir Spring mendukung beberapa mesin templating, tetapi saya hanya menggunakan JSP dalam hal yang serius. Melakukan desain "fuzzy MVC" Lift-terinspirasi dengan JSP akan menjadi gila. Ini adalah hal yang baik pada proyek yang lebih besar, di mana waktu untuk hanya membaca dan memahami bisa sangat melelahkan.

  2. Pilihan Mapper Object-Relational

    ORM bawaan lift adalah "Mapper". Ada alternatif mendatang yang disebut "Rekam", tapi saya pikir itu masih dianggap pra-alfa. Buku LiftWeb memiliki bagian tentang menggunakan Mapper dan JPA.

    Fitur CRUDify Lift , sejuk apa adanya, hanya berfungsi dengan Mapper (dan bukan JPA).

    Tentu saja, Spring mendukung persenjataan lengkap teknologi database standar dan / atau matang . Kata operatif ada "mendukung". Secara teoritis, Anda dapat menggunakan Java ORM dengan Lift, karena Anda dapat memanggil kode Java yang sewenang-wenang dari Scala. Tetapi Lift hanya benar-benar mendukung Mapper dan (sedikit banyak) JPA. Juga, bekerja dengan kode Java nontrivial di Scala saat ini tidak semulus yang diinginkan; menggunakan Java ORM, Anda mungkin akan menemukan diri Anda menggunakan kedua koleksi Java dan Scala di mana-mana atau mengonversi semua koleksi masuk dan keluar dari komponen Java.

  3. Konfigurasi

    Lift apps dikonfigurasikan sepenuhnya melalui metode kelas "Boot" aplikasi-lebar. Dengan kata lain, konfigurasi dilakukan melalui kode Scala. Ini sempurna untuk proyek dengan konfigurasi singkat, dan ketika orang yang melakukan konfigurasi merasa nyaman mengedit Scala.

    Musim semi cukup fleksibel dalam hal konfigurasi. Banyak opsi conf dapat didorong melalui konfigurasi XML atau anotasi.

  4. Dokumentasi

    Dokumentasi Lift masih muda. Dokumen Spring cukup matang. Tidak ada kontes.

    Karena dokumen Spring sudah tertata dengan baik dan mudah ditemukan, saya akan meninjau dokumen yang saya temukan untuk Lift. Pada dasarnya ada 4 sumber dokumentasi Lift: Buku LiftWeb , API Documents , grup Google LiftWeb , dan " Getting Started ". Ada juga paket contoh kode yang bagus, tapi saya tidak akan menyebutnya "dokumentasi" per se.

    Dokumen API tidak lengkap. Buku LiftWeb telah diterbitkan di atas pohon, tetapi juga tersedia secara online secara gratis. Ini sangat berguna, walaupun gaya didaktiknya yang jelas kadang membuat saya jengkel. Ini agak panjang di tutorial dan kontrak pendek. Spring memiliki manual yang tepat, yang tidak dimiliki Lift.

    Tetapi Lift memang memiliki serangkaian contoh yang bagus. Jika Anda merasa nyaman membaca kode Angkat dan kode contoh (dan Anda sudah tahu Scala dengan baik), Anda dapat menyelesaikannya dalam waktu yang cukup singkat.

Kedua kerangka kerja itu menarik. Ada berbagai aplikasi di mana Anda dapat memilih dan melakukannya dengan baik.

Dan LaRocque
sumber
10
Jawaban yang bagus, satu hal yang saya tidak setuju adalah: Musim semi menjadi kelas berat. Ini memiliki berbagai macam APIS yang baik, dan memaksakan cara struktural tertentu untuk bekerja diperlukan untuk mendapatkan hasil yang baik untuk itu, tetapi dibandingkan dengan hal-hal J2EE yang awalnya diganti itu adalah solusi yang jauh lebih ringan. Tentu saja "kelembutan" ada di mata yang melihatnya, jadi ini jelas merupakan argumen subyektif. Hanya 2 sen saya.
Brian
3
Jawaban yang bagus, sangat objektif. Angkat melakukan terlalu banyak hal yang tampaknya cerdas, yang sangat bodoh. Memindahkan logika halaman ke backend, mencampur HTML ke kode scala, yang lebih buruk daripada tag kontrol di dalam template halaman. Saya tidak tahu mengapa mereka melakukan ini, mungkin mereka berpikir Scala harus memproses XML dengan sangat cepat. "Panduan Definitif untuk Mengangkat" adalah buku teknologi terburuk yang pernah saya baca.
Sawyer
Saya tidak terbiasa dengan lift seperti yang saya inginkan. Menurut pendapat Anda seberapa sulitkah membuat pembungkus untuk objek boot yang membaca pengaturan dari file xml? Kesan pertama saya adalah karena scala menangani xml dengan sangat baik, itu tidak akan terlalu sulit.
Kera-inago
Sawyer, fakta bahwa Anda dapat mencampur HTML ke kode scala tidak mengatakan bahwa Anda harus. Menggunakan potongan dengan cara yang cerdas akan memisahkan kode HTML dan Scala. Tapi hei, tetap gunakan tag kontrol Anda;)
Alebon
1
@Dan, Apakah ada pembaruan sekarang karena Lift dua kali lebih tua dari ketika Anda pertama kali menulis ini?
Pacerier
229

Saya harus mengatakan bahwa saya sangat tidak setuju dengan jawaban Dan LaRocque.

Angkat bukan monolitik. Ini tersusun atas elemen-elemen diskrit. Itu tidak mengabaikan elemen J / EE, itu mendukung orang-orang seperti JNDI, JTA, JPA, dll. Fakta bahwa Anda tidak dipaksa untuk menggunakan elemen-elemen J / EE ini merupakan indikasi kuat dari desain modular Lift.

  • Filosofi pandangan Lift adalah "biarkan pengembang yang memutuskan." Lift menawarkan mekanisme templating yang tidak memungkinkan kode logika dalam tampilan, mekanisme tampilan berdasarkan mengeksekusi kode Scala dan XML literal Scala, dan mekanisme tampilan berdasarkan Scalate . Jika Anda memilih mekanisme templating XML, maka Anda memilih berapa banyak, jika ada, mark-up yang termasuk dalam logika bisnis Anda. Pemisahan tampilan Lift lebih kuat daripada apa pun yang ditawarkan Spring karena Anda tidak dapat mengekspresikan logika bisnis apa pun di templat XML Lift.
  • Objek Angkat ↔ Filsafat kegigihan adalah "biarkan pengembang yang memutuskan." Lift memiliki Mapper yang merupakan mapper relasional objek gaya ActiveRecord. Ini menyelesaikan pekerjaan untuk proyek-proyek kecil. Angkat dukungan JPA. Lift memiliki catatan abstraksi yang mendukung benda bolak-balik masuk dan keluar dari basis data relasional, masuk dan keluar dari toko NoSQL (Lift termasuk dukungan asli untuk CouchDB dan MongoDB, tetapi lapisan adaptor adalah beberapa ratus baris kode, jadi jika Anda ingin Cassandra atau sesuatu yang lain, itu tidak banyak pekerjaan untuk mendapatkannya.) Pada dasarnya, Angkat Kerangka Web tidak memiliki ketergantungan pada bagaimana objek dimaterialisasikan ke dalam sesi. Lebih lanjut, siklus sesi dan permintaan terbuka sehingga memasukkan kait transaksi ke dalam siklus permintaan / respons mudah.
  • Filosofi Lift adalah "tim server perlu tahu satu bahasa, bukan beberapa bahasa." Ini berarti konfigurasi dilakukan melalui Scala. Ini berarti bahwa kami tidak harus mengimplementasikan 40% dari konstruksi bahasa Jawa dalam sintaks XML untuk membuat opsi konfigurasi yang fleksibel. Ini berarti bahwa sintaksis kompiler dan ketik-periksa data konfigurasi sehingga Anda tidak mendapatkan parsing XML aneh atau data yang salah saat runtime. Artinya, Anda tidak harus memiliki IDE yang memahami rincian penjelasan yang Anda gunakan berdasarkan pustaka yang Anda gunakan.
  • Yap, dokumentasi Lift bukan kelebihannya.

Dengan kata-kata di atas, izinkan saya berbicara tentang filosofi desain Lift.

Saya menulis Manifesto Kerangka Web sebelum saya mulai menulis Lift. Untuk tingkat yang lebih tinggi, dan ke tingkat yang lebih besar daripada yang berlaku untuk kerangka kerja web lainnya yang saya ketahui, Lift memenuhi tujuan ini.

Angkat pada intinya berusaha mengabstraksi siklus permintaan / respons HTTP alih-alih menempatkan pembungkus objek di sekitar Permintaan HTTP. Pada tingkat praktis, ini berarti bahwa sebagian besar tindakan apa pun yang dapat dilakukan pengguna (mengirimkan elemen formulir, melakukan Ajax, dll.) Diwakili oleh GUID di browser dan fungsi di server. Ketika GUID disajikan sebagai bagian dari permintaan HTTP, fungsi diterapkan (disebut) dengan parameter yang disediakan. Karena GUID sulit diprediksi dan khusus untuk sesi, serangan replay dan banyak serangan pengrusakan parameter jauh lebih sulit dengan Lift daripada kebanyakan kerangka kerja web lainnya, termasuk Spring. Ini juga berarti bahwa pengembang lebih produktif karena mereka berfokus pada tindakan pengguna dan logika bisnis yang terkait dengan tindakan pengguna daripada plumbing pengepakan dan membongkar permintaan HTTP.

ajaxButton("Accept", () => {request.accept.save; 
                            SetHtml("acceptrejectspan", <span/>}) ++ 
ajaxButton("Reject", () => {request.reject.save; 
                            SetHtml("acceptrejectspan", <span/>})

Sesederhana itu. Karena friendRequest ada dalam ruang lingkup ketika fungsi dibuat, fungsi tersebut menutup ruang lingkup ... tidak perlu mengekspos kunci utama dari permintaan teman atau melakukan hal lain ... cukup tentukan teks tombol (itu dapat dilokalisasi atau dapat ditarik dari template XHTML atau dapat ditarik dari templat yang dilokalkan) dan fungsi untuk mengeksekusi ketika tombol ditekan. Lift dengan hati-hati menetapkan GUID, mengatur panggilan Ajax (melalui jQuery atau YUI, dan ya, Anda dapat menambahkan perpustakaan JavaScript favorit Anda sendiri), melakukan coba ulang otomatis dengan back-off, menghindari kelaparan koneksi dengan mengantri permintaan Ajax, dll.

Jadi, satu perbedaan besar antara Lift dan Spring adalah bahwa filosofi Lift tentang GUID yang terkait dengan fungsi memiliki manfaat ganda yaitu keamanan yang jauh lebih baik dan produktivitas pengembang yang jauh lebih baik. GUID -> Function asosiasi telah terbukti sangat tahan lama ... konstruksi yang sama berfungsi untuk bentuk normal, ajax, komet, penyihir multi-halaman, dll.

Bagian inti berikutnya dari Lift adalah menjaga abstraksi level tinggi sekitar selama mungkin. Di sisi pembuatan halaman, itu berarti membangun halaman sebagai elemen XHTML dan menjaga halaman sebagai XHTML sampai sebelum streaming tanggapan. Manfaatnya adalah penolakan terhadap kesalahan penulisan skrip situs, kemampuan untuk memindahkan tag CSS ke kepala dan skrip ke bagian bawah halaman setelah halaman dibuat, dan kemampuan untuk menulis ulang halaman berdasarkan browser target. Di sisi input, URL dapat ditulis ulang untuk mengekstrak parameter (parameter kueri dan path) dengan cara yang aman, tingkat tinggi, data keamanan diperiksa tersedia untuk diproses sangat awal dalam siklus permintaan. Misalnya, berikut ini cara mendefinisikan layanan permintaan REST:

  serve {
    case "api" :: "user" :: AsUser(user) :: _ XmlGet _ => <b>{user.name}</b>
    case "api" :: "user" :: AsUser(user) :: _ JsonGet _ => JStr(user.name)
  }

Menggunakan pencocokan pola bawaan Scala, kami mencocokkan permintaan masuk, mengekstrak bagian ketiga dari jalur dan mendapatkan Pengguna yang sesuai dengan nilai itu, dan bahkan menerapkan pemeriksaan kontrol akses (apakah sesi atau permintaan saat ini memiliki izin untuk mengakses yang diberikan Catatan pengguna). Jadi, pada saat contoh pengguna menyentuh logika aplikasi, itu diperiksa.

Dengan dua bagian inti ini, Lift memiliki keunggulan luar biasa dalam hal keamanan. Untuk memberi Anda gambaran tentang besarnya keamanan Lift yang tidak menghalangi fitur, Rasmus Lerdorg yang melakukan keamanan untuk Yahoo! mengatakan ini tentang FourSquare (salah satu situs poster-anak Lift):

Empat bintang ke @foursquare - situs pertama dalam beberapa waktu saya telah memperhatikan bahwa tidak ada masalah keamanan tunggal (yang dapat saya temukan) - http://twitter.com/rasmus/status/5929904263

Pada saat itu, FourSquare memiliki satu insinyur yang mengerjakan kode (bukan @harryh bukan super-genius) dan fokus utamanya adalah menulis ulang versi PHP FourSquare sambil mengatasi penggandaan lalu lintas mingguan.

Bagian terakhir dari fokus keamanan Lift adalah SiteMap. Ini adalah kontrol akses terpadu, navigasi situs, dan sistem menu. Pengembang menentukan aturan kontrol akses untuk setiap halaman menggunakan kode Scala (misalnya If(User.loggedIn _)atau If(User.superUser _)) dan aturan kontrol akses tersebut diterapkan sebelum rendering halaman dimulai. Ini sangat mirip dengan Spring Security, kecuali bahwa itu dipanggang sejak awal proyek dan aturan kontrol akses disatukan dengan aplikasi lainnya sehingga Anda tidak perlu memiliki proses untuk memperbarui aturan keamanan dalam XML ketika URL mengubah atau metode yang menghitung perubahan kontrol akses.

Untuk meringkas sejauh ini, filosofi desain Lift memberi Anda manfaat dari panggang dalam kontrol akses, resistensi terhadap 10 kerentanan keamanan OWASP, dukungan Ajax yang jauh lebih baik dan produktivitas pengembang yang jauh lebih tinggi daripada Spring.

Tetapi Lift juga memberi Anda dukungan Comet terbaik dari semua kerangka kerja web di sekitarnya. Itulah mengapa Novell memilih Lift untuk memberi daya pada produk Pulse mereka dan inilah yang dikatakan Novell tentang Lift:

Lift adalah jenis kerangka kerja web yang memungkinkan Anda sebagai pengembang untuk berkonsentrasi pada gambaran besar. Fitur pengetikan yang kuat, ekspresif, dan tingkat yang lebih tinggi seperti dukungan Comet bawaan memungkinkan Anda untuk fokus pada inovasi alih-alih pipa ledeng. Membangun aplikasi web yang kaya dan real-time seperti Novell Pulse membutuhkan kerangka kerja dengan kekuatan Lift di bawah selimut.

Jadi, Angkat bukan hanya kerangka kerja saya-terlalu MVC. Ini adalah kerangka kerja yang memiliki beberapa prinsip desain inti di belakangnya yang telah matang dengan sangat baik. Ini adalah kerangka kerja yang memberikan keuntungan ganda dari keamanan dan produktivitas pengembang. Lift adalah kerangka kerja yang dibangun berlapis-lapis dan memberi pengembang pilihan yang tepat berdasarkan kebutuhan mereka ... pilihan untuk generasi tampilan, pilihan untuk kegigihan, dll.

Scala dan Lift memberi pengembang pengalaman yang jauh lebih baik daripada perpaduan XML, anotasi, dan idiom lain yang membentuk Spring.

David Pollak
sumber
2
Versi arsip blog.lostlake.org/index.php?/archives/16-Web-Framework-Manifesto.html tersedia di replay.web.archive.org/20070220231839/http://blog.lostlake.org/…
Alan Hecht
1
Anda memiliki saya di dukungan asli MongoDB ... Saya berada di
Eran Medan
8
Saya sudah menulis webapps sejak pertengahan 90-an. Saya telah menggunakan Perl, Java, Seam, JSP, dan banyak lainnya. Semua orang yang saya kenal yang menggunakan pegas mengeluh tentang kesulitan mendapatkan konfigurasi dan dependensi yang benar, mimpi buruk, dll. Saya mulai menggunakan Lift pada sebuah proyek beberapa minggu yang lalu dan saya benar-benar kagum. Ini adalah kerangka kerja (dan bahasa) pertama yang saya gunakan di mana segala sesuatunya bekerja hampir tanpa usaha. Saya telah menulis lusinan fitur di aplikasi saya sejauh ini, dan saya kagum dengan betapa cepatnya saya melakukannya dengan benar ... bahkan dengan dokumen yang buruk dan pemahaman yang terbatas tentang Scala. Melihat adalah percaya.
Tony K.
@TonyK .: tentu saja Spring sangat berat, tetapi akan terbayar jika web-app berubah banyak kemudian, karena jauh lebih mudah untuk refactor / memperpanjang dengan Spring. IMHO, itu tergantung. Saya telah menggunakan beberapa kerangka kerja lain, seperti Grails, pada proyek-proyek kecil dan saya pikir itu sempurna untuk itu - tetapi untuk sesuatu akan banyak berubah nanti, saya akan pergi dengan Spring.
Menunggu Long
7
@ HoàngLong Ada banyak pendekatan untuk kesulitan evolusi perangkat lunak. Saya hanya menyatakan pengalaman saya: Jawa menunjukkan usia itu sebagai bahasa, seperti halnya kerangka kerja yang dibangun di atasnya. Waktu untuk berevolusi menjadi hal-hal yang lebih ekspresif dan kuat tanpa semua boilerplate dan konfigurasi kegilaan.
Tony K.
11

Saya akan merekomendasikan Anda untuk memeriksa kerangka permainan, ia memiliki beberapa ide yang sangat menarik dan mendukung pengembangan di Jawa dan Scala

IPC
sumber
2
Saya memeriksa Play. Saya tidak melihat apa pun kecuali kelas bawaan yang memuat ulang untuk merekomendasikannya, dan dengan lisensi Scala JRebel gratis, Anda mendapatkannya dengan Lift.
Tony K.
10

Hanya untuk bersenang-senang. Dan demi mempelajari pendekatan pemrograman baru.

Roma
sumber
10

Saya sangat ingin menggunakan Lift untuk proyek web baru-baru ini, bukan penggemar berat Spring MVC. Saya belum pernah menggunakan versi terbaru, tetapi versi Spring MVC sebelumnya membuat Anda melompati banyak rintangan untuk menjalankan aplikasi web. Saya hampir dijual di Lift sampai saya melihat bahwa Lift bisa sangat tergantung sesi dan membutuhkan 'sesi lengket' untuk bekerja dengan benar. Kutipan dari http://exploring.liftweb.net/master/index-9.html#sec:Session-Management

Sampai ada teknologi replikasi sesi standar Anda masih dapat mengelompokkan aplikasi Anda menggunakan "sesi lengket". Ini menyatakan bahwa semua permintaan yang berkaitan dengan sesi HTTP harus diproses oleh node cluster yang sama

Jadi begitu Sesi diperlukan, pengguna harus disematkan ke simpul itu. Ini menciptakan kebutuhan untuk penyeimbangan muatan yang cerdas dan mempengaruhi penskalaan, yang mencegah Lift menjadi solusi dalam kasus saya. Saya akhirnya memilih http://www.playframework.org/ dan sangat senang. Bermain telah stabil dan dapat diandalkan sejauh ini dan sangat mudah untuk dikerjakan.

mguymon
sumber
7

Saya tidak datang ke Lift dan Scala dari latar belakang Java, jadi ini bukan dari pengalaman pribadi, tetapi saya tahu bahwa banyak pengembang Lift menemukan Scala sebagai bahasa yang jauh lebih ringkas dan efisien daripada Java.

pr1001
sumber
3

Memperluas pengetahuan Anda selalu merupakan upaya yang bermanfaat :) Saya baru saja mulai belajar Scala, ini memengaruhi cara saya menulis Java normal dan saya bisa mengatakan itu sangat bermanfaat sejauh ini.

gpampara
sumber
3

Saya benci untuk benar-benar melemparkan duniamu untuk satu putaran. Tapi Anda bisa menggunakan Scala, Java, Lift, Spring dalam satu aplikasi dan tidak menjadi masalah.

Berlin Brown
sumber
0

Menurut pendapat saya yang sederhana, imajinasi adalah yang terpenting.

Mari kita pertimbangkan Anda ingin menulis aplikasi. Jika Anda seorang pengembang yang layak, aplikasi seharusnya sudah dibangun di pikiran Anda. Langkah selanjutnya adalah menemukan cara kerjanya melalui kode. Untuk melakukan itu, Anda harus melewati aplikasi yang dibayangkan melalui fungsi yang menerjemahkannya ke aplikasi dunia nyata. Fungsi itu adalah bahasa pemrograman. Begitu

Real app = programming language (imagined app)

Jadi pilihan bahasa itu penting. Begitu juga kerangka kerjanya. Ada banyak orang pintar di sini yang akan memberi tahu Anda tentang apa yang harus dipilih, tetapi pada akhirnya, bahasa / kerangka kerja yang paling baik menerjemahkan imajinasi Anda harus menjadi pilihan Anda. Jadi prototipe dengan keduanya dan buat pilihan Anda.

Sedangkan bagi saya, saya perlahan-lahan mempelajari Scala dan Angkat dan menyukainya.

Matei Alexandru Bogdan
sumber
0

Tapi masalah utamanya adalah kita tidak bisa membandingkan pegas dengan lift. Lift pada dasarnya digunakan sebagai kerangka kerja UI dan Spring digunakan sebagai kerangka kerja DI.
Jika Anda mengembangkan aplikasi web yang memiliki banyak backend yakin Anda dapat menggunakan lift.
tetapi jika aplikasi web Anda mengembangkan yang memiliki beberapa seri backend dan Anda pasti perlu kebagian musim semi.

Rajith Delantha
sumber