JudoScript: Scripting for Java and Beyond
The Java platforms (J2SE and J2EE) are filled with rich and useful features, making Java an extremely useful resource for more than mere programming. To use Java, a good scripting tool is needed. JudoScript was developed to fill this need. It is a third and fourth generation tool (3GL and 4GL), providing normal programming support. In addition, it provides special mechanisms for various computing needs, such as Java scripting, JDBC scripting, XML scripting, HTML/SGML scraping, file and archive manipulation, versatile command-line execution, scheduling, HTTP handling, Java GUI scripting and many other utilities. JudoScript also is programmer-friendly and supports thread programming, useful data structures and so on. Its programming model is similar to that of Java and Python.
JudoScript was first released in November of 2001. You may wonder why we need a new scripting language? The truth is, with Java, we do not need another programming language, but we do need a good scripting tool.
Going through the Java packages shipped with J2SE and J2EE, one finds functionalities from basic computing to enterprise computing. Open-source projects provide even more cool functionalities, so we can do SSH/SCP, PDF generation, FTP, Telnet, Windows registry, UNIX syslog and much more. All in Java, Java has become a vast pool of readily usable resources for any platforms that runs it.
Coding in Java, however, is not a feasible solution. Often times we need to quickly fix up something, such as going through a directory tree to do some file manipulation, or checking a database for unprocessed orders or retrieving information in an XML document to create a report. For these types of one-time actions, rigorous Java coding is an overkill. Other times, we create scripts for repetitive tasks, which are run either manually or automatically. These scripts update frequently and performance is not critical. Edit-and-run can drastically improve productivity and reduce stress.
Another problem is the verbosity of Java programs. Java is a so-called system language, with low-level, fine-granularity APIs and language constructs that enforce good software engineering. But these elements become obstacles in our situations. On a related note, coding in Java requires expertise that is at times not available or desirable. Imagine trying to put a few files into a ZIP file using Java, without prior knowledge of the java.util.zip package. You would need to understand the files' meanings and relationships before you could do anything with them. As a user, I know Java can do ZIP files, but I don't really care whether java.util.zip.ZipFile or bill.wonderutils.ZipArchive is used.
So, really, what we need is a good scripting tool that allows us to easily use the many features in Java, standard or open source, with maximum flexibility (i.e., programmability) and extensibility.
Use Java as scripting. A few projects use Java as-is for scripting. This idea misses the fact that Java is a system language; edit-and-run alone does not legitimize Java as a scripting language.
Clone Java for scripting. Beal Shell and JavaScript fall into this category. Their syntax is designed to come very close to Java, but they use typeless variables. This helps in certain coding situations, but it doesn't meet our needs for using Java and beyond.
Port existing languages to Java. JavaScript, Python and TCL are a few examples. These are considered 3GLs. Again, they are not good enough to use Java's rich functionality. We need something better.
The easiest way to solve problems is to tell an expert and have him do it for you. This idea reflects the philosophy of so-called 4GLs, which are domain-specific languages. They allow users to specify intentions, and the language engine provides solutions automatically. A typical example is SQL. You write a query; the SQL engine returns you rows of data. You don't need to open tables, locate available indices, check against the rows and create a cache for large amounts of data.
Opposite of 4GLs, 3GLs allow programmers to specify solutions in the form of algorithms. Most major programming languages, including scripting languages, are 3GLs. 3GL design is what I refer to as "orthogonal"; that is, the language has a small core syntax set and a standard extension mechanism (usually known as library or package) that allows the language to expand in functionality. Thus, a 3GL has few functionalities and uses on its own; they almost always come with a standard library to make it minimally useful, such as the C run-time library, standard Java packages and standard Python packages. This type of design fulfills the beauty of minimalism, at the expense of ease-of-use for non-coders (or coders who prefer not to code all the time).
JudoScript, on the other hand, embodies a special syntax for popular modern computing areas, such as JDBC scripting, sendmail, HTML/SGML scraping and GUI scripting. In other words, it takes a 4GL approach for popular functional areas. Because of such intimate support, scripting in these areas is more effective and elegant.
JudoScript is a genuine 3GL, too. It is an advanced programming language, with a hybrid object-oriented, procedural and thread programming model. It can directly use Java code.
UNIX shells have a 4GL flavor. They are legitimate programming languages and provide commands for tasks common to all system users. JudoScript serves a similar purpose for Java as shells do for UNIX, except Java and JudoScript go beyond the basics. They can use the latest computing trends, extend their reaches into application areas and run on multiple platforms.
JudoScript's many features can be grouped into three areas: general programming, Java scripting and application scripting.
1. General Programming Features
JudoScript supports typeless variables with typed values. Each value is an object of some class. The primitive data types include integral and floating-point numbers, string and date/time. A value also can be an instance of any user-defined class, Java object or array, function reference, or any of its built-in object types and data structures.
array | one- or multi-dimensional linear container |
linkedList | same as array, but implemented as a linked list |
struct | a map; is the root of all user-defined classes |
orderedMap | same as struct, except the order of added keys is retained |
stack | a first-in-last-out container |
queue | a first-in-first-out container |
tableData | a two-dimensional array with column captions and methods for retrieving rows or columns |
treeNode | a convenience for building trees |
Classes and functions can be declared anywhere, in any order. Classes can extend others; the root of user-defined class is either struct or orderedMap. Classes are nothing but a struct associated with a number of methods. It is possible to transpose an object from one class to another after it is instantiated.
Expressions in JudoScript are similar to those in Java or C; so are flow control statements. The parentheses for the expression in if-else, switch, while and for statements are optional. There also is an extended loop statement for enumerating array elements.
arr = { 0, 1, 2, 3, 4 }; for x in arr { switch x { case 0: println 'zero'; break; case 1: println 'uno'; break; default: println x; break; } }
Functions can be referenced and stored in variables. This can be used for, say, special sorting of arrays:
a = { '1.2', '3', '1.10', '1.2.1', '2.3' }; a.sort( &my_comparator ); println a; function my_comparator(lhs, rhs) { la = lhs.csv('.'); ra = rhs.csv('.'); for i from 0 to la.size() { if la[i].int() < ra[i].int() { return -1; } if la[i].int() > ra[i].int() { return 1; } } if la.size() == ra.size() { return 0; } return la.size() > ra.size(); }
In this example, we want 1.10 to appear after 1.2. Had we used array's default sort() method, it would be an alphabetical sort and 1.10 would go first.
Exceptions can be thrown by the JudoScript language engine or by the executed Java code. They can be captured and handled. JudoScript also offers a "resume" statement, which is not found in Java.
{ a = 1; println 'a + "abc" = ', a + "abc"; // exception println 'abc' @ a = ', "abc" @ a; // concat catch: println 'EXCEPTION: ', $_.message; resume; finally: println 'Done.'; } //The result: // a + "abc" = EXCEPTION: Invalid number format: abc // 'abc' @ a = abc1 // Done.
Thread programming in JudoScript is easy: a thread is declared in the same way that a function is declared. The following example shows a multithreaded HTTP server:
println <err> 'Port: 8088'; docRoot = '/var/share/'; ss = startServer(8088); while { start thread hndlr(acceptHttp(ss)); } thread hndlr req { req.serveFile(docRoot); } //Singled-threaded HTTP server, use these: // while { hndlr(acceptHttp(ss)); } // function hndlr req { req.serveFile(docRoot); }
2. Java Scripting
Java code can be used directly in JudoScript. Some people are happily surprised when they learn they can run Java without having to compile the code.
hash = javanew java.util.Hashtable; hash.put( 'date', date(2001,1,1) ); hash.put( 'integer', 1 ); hash.put( 'double', 10.01 ); hash.put( 'string', 'Hello, World!' ); keys = hash.keys(); while keys.hasMoreElements() { k = keys.nextElement(); println k, ' = ', hash.get(k); }
Java arrays can be created and used the same way as native JudoScript arrays:
ba = javanew byte[]{ 0, 1, 2 }; for x in ba { println x; } oa = javanew java.lang.Object[3]; oa[0] = javanew java.io.File('abc'); oa[1] = java.lang.System::out; oa[2] = javanew java.util.Hashtable; for x in oa { println x; }
As you have seen, static members of a Java class are accessed by the :: operator. You even can alias a Java static method to become a JudoScript function; some of the JudoScript system functions are indeed Java static methods.
function gc for java.lang.System::gc(); function props for java.lang.System::getProperties(); function getProp for java.lang.System::getProperty();
System functions, including initialContext() and weblogicInitialContext() are designed specifically for EJB and JNDI client scripting.
const #OrderHome = 'com.myecom.OrderHome'; ctx = weblogicInitialContext('t3://server','user','pass'); home = ctx.lookup(#OrderHome); key = javanew com.myecom.OrderKey('00134'); order = home.findByPrimaryKey(key); // ...
JudoScript does not require long list of imports or declaration of variables with long class names. Nor does it require casting everywhere and exceptions catching. Combined with JDBC scripting, for things like querying for order keys, your scripts can do a lot of powerful work.
In cases where you truly need Java, such as a special GUI component or extension of an abstract Java class, you can write embedded Java class in JudoScript. An adapter mechanism is available that allows JudoScript to implement Java interfaces, but a Java compiler must be available for these purposes. Conversely, JudoScript can be embedded in other Java software, through its language engine interface must supports the Bean-Script-Framework (BSF).
The capability to use Java directly is significant, because this makes all Java software--what's on the market today and what's available next year--developed tomorrow, available to JudoScript.
3. JudoScript Application Features
The following is an incomplete list of JudoScript application features:
JDBC scripting
XML SAX-style scripting, DOM programming and XSLT
HTML/SGML scraping
Sendmail
HTTP server and client handling, including cookies
Scheduling
Execution of command lines
SSH and SCP (via ISNetworks' MindTerm package)
File operations in filesystems and in ZIP, TAR or GZIP archives
Copying of files and directories between combinations of file systems and ZIP or TAR archives
Encryption and decryption (based on JCE)
Java GUI scripting
Some of these features are implemented as system functions and objects, like the one in the HTTP server example above. Some use straightforward syntax, such as sendmail, files and archives.
sendmail to: 'tom@tom_jerry.org, jerry@tom_jerry.org' cc: 'mike@tom_jerry.org' from: 'james@tom_jerry.org' subject: 'Test test' attach: 'data.zip, readme.txt' body: [[* Hi Tom and Jerry, Attached is the data and usage. Enjoy! -James *]] htmlBody: [[* <html><body> <p>Hi Tom and Jerry,</p> <p>Attached is the data and usage. <b>Enjoy!</b></p> <p>-James</p> </body></html> *]] ;
// copy original Java files into src.jar copy '*.java, *.judo' except '*/parser/*.java' in srcDir recursive noHidden echo into 'src.jar'; // archive original Java files and docs jarFile = createJar('save.jar'); copy '*.java, *.judo' except '*/parser/*.java' in srcDir recursive noHidden echo into jarFile under 'src/'; copy '*' in docDor recursive noHidden echo into jarFile under 'doc/'; jarFile.close();
XML SAX scripting, HTML scraping and Java GUI scripting use an event-driven model, and GUI programs generate events. For XML and HTML documents, the parser creates an "event" for each tag, which includes text, and JudoScript specifies handlers for these events.
// print out links and text lengths in HTML do 'http://www.yahoo.com' as sgml { <a>: println 'URL: ", $_.href; <img>: println 'URL: ", $_.src; <link>: println 'URL: ", $_.src; TEXT: println 'Text: length=', $_.length(); }
This HTML/SGML scripting feature has been used extensively to produce JudoScript's own documentation.
// A Swing GUI that does nothing. frame = javanew javax.swing.JFrame("Hi!"); frame.setVisible(true); guiEvents { <frame : Window : windowClosing> : exit(0); }
The built-in variable $_ representes the current event. In the case of HTML or XML, it represents the tag itself. In GUI programs, it is the event object in respective listener methods.
A few other areas involve more complicated, special-purpose statements. This includes scheduling, command-line execution and JDBC scripting.
JDBC scripting is one of the major inspirations for this language. SQL statements can be executed easily in JudoScript, either individually, in a group or in a batch. Statements can be prepared, then executed with bound variables. Stored procedures can be executed in the same ways. The following example demonstrates creating tables, as well as inserting and querying with both prepared and direct executions. Note the JDBC driver is loaded automatically based on the URL, because JudoScript has knowledge about many JDBC drivers.
{ // try block. connect 'jdbc:mysql://localhost/mydb', 'user', 'pass'; // (re-)create tables tables = { 'participants', 'media', 'people' }; for x in tables { // drop tables if exist already if tableExists(x) { executeSQL: drop table (* x *); } } executeSQL { create table people ( personID int primary key, name varchar(20) not null, alias varchar(20), sex varchar(1), // 'M', 'F' addTime date ); create table media ( folderID varchar(255) not null, fileName varchar(128) not null, type varchar(1) not null, fileTime date, bytes int, addTime date, constraint pathName primary key (folderID,fileName) ); create table participants ( folderID varchar(255), fileName varchar(128), personID int, constraint ref_media foreign key (folderID,fileName) references media (folderID,fileName), constraint ref_people foreign key (personID) references people (personID) ); } // end of executeSQL. // insert prepare mediatbl: insert into media (folderID,fileName,type,fileTime,bytes,addTime) values (?,?,?,?,?,now()) ; executeUpdate mediatbl with @1 = '/2001-06', // default type is "string". @2 = 'DSC00001.jpg', @3 = 'I', @4:date = date(2001,6,3), @5:int = 45044 ; println unit(mediatbl.updateCount,'row'), " inserted into 'media' table."; preparedExecuteUpdate partbl: // one-step prepare & execute. insert into participants (folderID,fileName,personID) values (?,?,?) ; with @1 = '/2001-06', @2 = 'DSC00001.jpg', @3:int = 1 // for alias 'bear' ; println unit(partbl.updateCount,'row'), " inserted into 'participants' table."; // query executeQuery q: select * from people order by alias ; while q.next() { println 'personID: ', q[1]; println ' name: ', q[2]; println ' alias: ', q[3]; println ' sex: ', q.SEX; println ' addTime: ', q.addTime, nl, '--------'; } catch: $_.printStackTrace(); finally: disconnect(); } // end of try block.
This article is a rather high-level introduction to JudoScript, with simple examples that reveal only a taste of what the language can accomplish. JudoScript's language design is different from other 3GL scripting languages because of its willingness to take in a special syntax for specific functional areas. It also is a powerful programming language. Combine these two aspects, and you achieve great computing power.
The applications and abilities of JudoScript, released under the LGPL, continue to grow. We are working on integrating JudoScript with more LGPL or GPL open-source Java projects. The principle is simple: if something is useful and available, JudoScript will make it easier to use and readily scriptable.
For complete information and documentation, visit www.judoscript.com.
James Huang is the author of JudoScript. He conceived, designed and implemented the language single-handedly. He has been doing commercial Java programming for six years and web development for four years.