Bahasa skrip Java: Mana yang tepat untuk Anda?

Beberapa persyaratan aplikasi Java mengharuskan integrasi dengan bahasa skrip. Misalnya, pengguna Anda mungkin perlu menulis skrip yang menggerakkan aplikasi, memperluasnya, atau berisi loop dan konstruksi kontrol aliran lainnya. Dalam kasus seperti itu, sebaiknya Anda mendukung penerjemah bahasa skrip yang dapat membaca skrip pengguna, lalu menjalankannya pada kelas aplikasi Java Anda. Untuk menyelesaikan tugas itu, jalankan penerjemah bahasa skrip berbasis Java di JVM yang sama dengan aplikasi Anda.

Library pendukung, seperti IBM's Bean Scripting Framework atau library Ramnivas Laddad yang dikembangkan dalam "Scripting Power Save the Day for Your Java Apps" ( JavaWorld, Oktober 1999), berhasil membantu Anda memasukkan bahasa skrip yang berbeda ke dalam program Java Anda. Kerangka kerja tersebut tidak memerlukan perubahan besar pada aplikasi Java Anda, dan memungkinkan program Java Anda menjalankan skrip yang ditulis dalam Tcl, Python, dan bahasa lain.

Skrip pengguna juga bisa langsung mereferensikan kelas aplikasi Java Anda, sama seperti jika skrip tersebut adalah bagian lain dari program Anda. Itu bagus dan buruk. Ada baiknya jika Anda ingin skrip untuk mendorong pengujian regresi terhadap program Anda dan perlu melakukan panggilan tingkat rendah dari skrip ke dalam aplikasi Anda. Ini buruk jika skrip pengguna beroperasi pada internal program Anda dan bukan terhadap API yang telah disepakati, sehingga membahayakan integritas program Anda. Jadi, rencanakan untuk menerbitkan API yang Anda ingin pengguna Anda tuliskan skripnya dan klarifikasi bahwa program lainnya tetap terlarang. Anda juga dapat mengaburkan nama kelas dan metode yang Anda tidak ingin pelanggan menulis skrip, tetapi biarkan kelas API dan nama metode saja. Dengan melakukan itu, Anda 'akan memperkecil kemungkinan pengguna petualang akan membuat kode terhadap kelas yang tidak Anda inginkan.

Mengaitkan beberapa bahasa skrip ke dalam program Java Anda luar biasa, tetapi pikirkan dua kali jika Anda menulis aplikasi komersial - Anda membuka kaleng worm dengan mencoba menjadi segalanya bagi semua pengguna. Anda harus mempertimbangkan masalah manajemen konfigurasi, karena setidaknya beberapa penerjemah skrip diperbarui dan dirilis secara berkala. Jadi, Anda harus memastikan versi mana dari setiap penerjemah skrip yang sesuai dengan rilis aplikasi Anda. Jika pengguna menempatkan versi yang lebih baru dari salah satu interpreter ini di pohon pemasangan aplikasi Anda (dengan harapan memperbaiki bug di versi yang lebih lama), mereka sekarang akan menjalankan konfigurasi yang belum teruji dari aplikasi Java Anda. Berhari-hari atau berminggu-minggu kemudian, ketika pengguna menemukan dan melaporkan bug dalam aplikasi Anda yang ditemukan oleh versi juru bahasa skrip yang lebih baru,mereka mungkin tidak akan menyebutkan perubahan juru bahasa skrip kepada staf dukungan pelanggan Anda - sehingga sulit bagi teknisi Anda untuk mereproduksi masalah tersebut.

Selain itu, pelanggan kemungkinan besar akan bersikeras Anda menawarkan perbaikan bug untuk juru bahasa skrip yang didukung aplikasi Anda. Beberapa interpreter secara aktif dipelihara dan diperbarui melalui model open source; dalam kasus tersebut para ahli dapat membantu Anda mengatasi masalah, menambal penerjemah, atau mendapatkan perbaikan bug yang disertakan dalam rilis mendatang. Itu penting karena tanpa dukungan, Anda dapat terjebak dengan tugas yang tidak menyenangkan untuk memperbaiki masalah sendiri, dan penerjemah skrip menjalankan antara 25.000 hingga 55.000 baris kode.

Untuk menghindari skenario perbaiki sendiri, Anda dapat menguji secara menyeluruh setiap penerjemah skrip yang ingin Anda dukung dengan aplikasi Anda. Untuk setiap interpreter, pastikan bahwa interpreter dengan anggun menangani skenario penggunaan yang paling umum, potongan memori yang besar tidak bocor ketika Anda menggunakan interpreter dengan skrip yang panjang dan menuntut, dan tidak ada yang tidak terduga yang terjadi ketika Anda memasukkan interpreter program dan scripting tangan penguji beta yang menuntut. Ya, pengujian di muka seperti itu membutuhkan waktu dan sumber daya; namun, pengujian menghabiskan waktu dengan baik.

Solusinya: Buat tetap sederhana

Jika Anda harus mendukung pembuatan skrip dalam aplikasi Java Anda, pilih juru bahasa skrip yang paling sesuai dengan kebutuhan aplikasi dan basis pelanggan Anda. Dengan demikian, Anda menyederhanakan kode integrasi-interpreter, mengurangi biaya dukungan pelanggan, dan meningkatkan konsistensi aplikasi Anda. Pertanyaan sulitnya adalah: jika Anda harus melakukan standarisasi hanya pada satu bahasa scripting, mana yang Anda pilih?

Saya membandingkan beberapa penerjemah skrip, dimulai dengan daftar bahasa termasuk Tcl, Python, Perl, JavaScript, dan BeanShell. Kemudian, tanpa melakukan analisis mendetail, saya tidak mempertimbangkan Perl. Mengapa? Karena tidak ada interpreter Perl yang ditulis di Java. Jika juru bahasa skrip yang Anda pilih diimplementasikan dalam kode asli, seperti Perl, maka interaksi antara aplikasi Anda dan kode skrip kurang langsung, dan Anda harus mengirimkan setidaknya satu biner asli dengan program Java Anda untuk setiap sistem operasi yang Anda pedulikan. Karena banyak pengembang memilih Java karena portabilitas bahasanya, saya tetap setia pada keuntungan itu dengan tetap menggunakan penerjemah skrip yang tidak membuat ketergantungan pada biner asli. Java adalah lintas platform, dan saya ingin penerjemah skrip saya juga. Sebaliknya,Penerjemah berbasis Java memang ada untuk Tcl, Python, JavaScript, dan BeanShell, sehingga mereka dapat berjalan dalam proses dan JVM yang sama seperti aplikasi Java Anda lainnya.

Berdasarkan kriteria tersebut, daftar perbandingan interpreter scripting terdiri dari:

  • Jacl: Implementasi Tcl Java
  • Jython: Implementasi Python Java
  • Rhino: Penerapan JavaScript Java
  • BeanShell: Juru bahasa sumber Java yang ditulis di Java

Sekarang kita telah memfilter daftar bahasa penerjemah skrip ke Tcl, Python, JavaScript, dan BeanShell, yang membawa kita ke kriteria perbandingan pertama.

Tolok ukur pertama: Kelayakan

Untuk tolok ukur pertama, kelayakan, saya memeriksa keempat penafsir untuk melihat apakah ada yang membuat mereka tidak mungkin digunakan. Saya menulis program pengujian sederhana dalam setiap bahasa, menjalankan kasus pengujian saya terhadap mereka, dan menemukan bahwa masing-masing bekerja dengan baik. Semua bekerja dengan andal atau terbukti mudah diintegrasikan. Meskipun setiap penafsir tampaknya merupakan kandidat yang layak, apa yang akan membuat pengembang memilih satu sama lain?

  • Jacl: Jika Anda menginginkan konstruksi Tk dalam skrip Anda untuk membuat objek antarmuka pengguna, lihat proyek Swank untuk kelas Java yang membungkus widget Swing Java ke dalam Tk. Distribusi tidak menyertakan debugger untuk skrip Jacl.
  • Jython: Mendukung skrip yang ditulis dalam sintaks Python. Alih-alih menggunakan tanda kurung kurawal atau penanda ujung-awal untuk menunjukkan aliran kontrol, seperti yang dilakukan banyak bahasa, Python menggunakan tingkat indentasi untuk menunjukkan blok kode mana yang dimiliki bersama. Apa itu masalah? Itu tergantung pada Anda dan pelanggan Anda dan apakah Anda keberatan. Distribusi tidak menyertakan debugger untuk skrip Jython.
  • Rhino: Banyak programmer mengasosiasikan JavaScript dengan pemrograman Halaman Web, tetapi versi JavaScript ini tidak perlu dijalankan di dalam browser Web. Saya tidak menemukan masalah saat mengerjakannya. Distribusi dilengkapi dengan debugger skrip yang sederhana namun bermanfaat.
  • BeanShell: Pemrogram Java akan langsung merasa nyaman dengan perilaku juru bahasa sumber ini. Dokumentasi BeanShell dilakukan dengan baik, tetapi jangan mencari buku tentang pemrograman BeanShell di toko buku Anda - tidak ada. Dan tim pengembangan BeanShell juga sangat kecil. Namun, itu hanya masalah jika para pelaku beralih ke minat lain dan orang lain tidak turun tangan untuk mengisi posisi mereka. Distribusi tidak menyertakan debugger untuk skrip BeanShell.

Tolok ukur kedua: Performa

Untuk tolok ukur kedua, kinerja, saya memeriksa seberapa cepat juru bahasa skrip mengeksekusi program sederhana. Saya tidak meminta penerjemah untuk mengurutkan array besar atau melakukan matematika yang rumit. Sebaliknya, saya terjebak pada tugas-tugas dasar dan umum seperti perulangan, membandingkan bilangan bulat dengan bilangan bulat lainnya, dan mengalokasikan dan menginisialisasi array satu dan dua dimensi yang besar. Tidak sesederhana itu, dan tugas-tugas ini cukup umum sehingga sebagian besar aplikasi komersial akan melakukannya pada satu waktu atau lainnya. Saya juga memeriksa untuk melihat berapa banyak memori yang dibutuhkan setiap interpreter untuk instantiation dan untuk menjalankan skrip kecil.

Untuk konsistensi, saya membuat kode setiap tes semirip mungkin di setiap bahasa scripting. Saya menjalankan pengujian pada laptop Toshiba Tecra 8100 dengan prosesor 700-MHz Pentium III dan 256 MB RAM. Saat menjalankan JVM, saya menggunakan ukuran heap default.

Untuk kepentingan menawarkan perspektif seberapa cepat atau lambat angka-angka ini, saya juga mengkodekan kasus uji di Java dan menjalankannya menggunakan Java 1.3.1. Saya juga memutar ulang skrip Tcl yang saya tulis untuk juru bahasa skrip Jacl di dalam juru bahasa Tcl asli. Akibatnya, pada tabel di bawah ini, Anda dapat melihat bagaimana interpreter dibandingkan dengan interpreter native.

Tabel 1. Untuk penghitungan putaran dari 1 hingga 1.000.000
Penerjemah skrip Waktu
Jawa 10 milidetik
Tcl 1,4 detik
Jacl 140 detik
Jython 1,2 detik
Badak 5 detik
BeanShell 80 detik
Tabel 2. Bandingkan 1.000.000 bilangan bulat untuk persamaan
Penerjemah skrip Waktu
Jawa 10 milidetik
Tcl 2 detik
Jacl 300 detik
Jython 4 detik
Badak 8 detik
BeanShell 80 detik
Tabel 3. Alokasikan dan inisialisasi 100.000 elemen array
Penerjemah skrip Waktu
Jawa 10 milidetik
Tcl .5 detik
Jacl 25 detik
Jython 1 detik
Badak 1,3 detik
BeanShell 22 detik
Tabel 4. Alokasikan dan inisialisasi array elemen 500 x 500
Penerjemah skrip Waktu
Jawa 20 milidetik
Tcl 2 detik
Jacl 45 detik
Jython 1 detik
Badak 7 detik
BeanShell 18 detik
Tabel 5. Memori yang dibutuhkan untuk menginisialisasi interpreter di JVM
Penerjemah skrip Ukuran memori
Jacl Sekitar 1 MB
Jython Sekitar 2 MB
Badak Sekitar 1 MB
BeanShell Sekitar 1 MB

Arti angka-angka itu

Jython membuktikan yang tercepat pada benchmark dengan selisih yang cukup besar, dengan Rhino di posisi kedua yang cukup dekat. BeanShell lebih lambat, dengan Jacl di bagian belakang.

Whether these performance numbers matter to you depends on the tasks you want to do with your scripting language. If you have many hundreds of thousands of iterations to perform in your scripting functions, then Jacl or BeanShell might prove intolerable. If your scripts run few repetitive functions, then the relative differences in speeds between these interpreters seem less important.

It's worth mentioning that Jython doesn't seem to have built-in direct support for declaring two-dimensional arrays, but this can be worked around by using an array-of-arrays structure.

Although it was not a performance benchmark, it did take me more time to write the scripts in Jython than for the others. No doubt my unfamiliarity with Python caused some of the trouble. If you are a proficient Java programmer but are unfamiliar with Python or Tcl, you may find it easier to get going writing scripts with JavaScript or BeanShell than you will with Jython or Jacl, since there is less new ground to cover.

The third benchmark: Integration difficulty

The integration benchmark covers two tasks. The first shows how much code instantiates the scripting language interpreter. The second task writes a script that instantiates a Java JFrame, populates it with a JTree, and sizes and displays the JFrame. Although simple, these tasks prove valuable because they measure the effort to start using the interpreter, and also how a script written for the interpreter looks when it calls Java class code.

Jacl

To integrate Jacl into your Java application, you add the Jacl jar file to your classpath at invocation, then instantiate the Jacl interpreter prior to executing a script. Here's the code to create a Jacl interpreter:

import tcl.lang.*; public class SimpleEmbedded { public static void main(String args[]) { try { Interp interp = new Interp(); } catch (Exception e) { } } 

The Jacl script to create a JTree, put it in a JFrame, and size and show the JFrame, looks like this:

package require java set env(TCL_CLASSPATH) set mid [java::new javax.swing.JTree] set f [java::new javax.swing.JFrame] $f setSize 200 200 set layout [java::new java.awt.BorderLayout] $f setLayout $layout $f add $mid $f show 

Jython

To integrate Jython with your Java application, add the Jython jar file to your classpath at invocation, then instantiate the interpreter prior to executing a script. The code that gets you this far is straightforward:

import org.python.util.PythonInterpreter; import org.python.core.*; public class SimpleEmbedded { public static void main(String []args) throws PyException { PythonInterpreter interp = new PythonInterpreter(); } } 

The Jython script to create a JTree, put it in a JFrame, and show the JFrame is shown below. I avoided sizing the frame this time:

from pawt import swing import java, sys frame = swing.JFrame('Jython example', visible=1) tree = swing.JTree() frame.contentPane.add(tree) frame.pack() 

Rhino

As with the other interpreters, you add the Rhino jar file to your classpath at invocation, then instantiate the interpreter prior to executing a script:

import org.mozilla.javascript.*; import org.mozilla.javascript.tools.ToolErrorReporter; public class SimpleEmbedded { public static void main(String args[]) { Context cx = Context.enter(); } } 

The Rhino script to create a JTree, put it in a JFrame, and size and show the JFrame proves simple:

importPackage (java.awt); importPackage (Packages.javax.swing); frame = Frame baru ("JavaScript"); frame.setSize (Dimensi baru (200,200)); frame.setLayout (new BorderLayout ()); t = new JTree (); frame.add (t, BorderLayout.CENTER); frame.pack (); frame.show ();