|
Listing 4.1 cmdline.judo |
---|
print '#cmd_args: '; for x in #cmd_args { print x, ' '; } print nl, ' #args: '; for x in #args { print x, ' '; } println nl, ' #options: '; for x in #options.keys() { println ' ', x, '=', #options[x]; } |
Let's run with this command line:
java judo -q cmdline.judo uvw -aa:1 -bb -aa:2 xyz
The result is:
#cmd_args: uvw -aa:1 -bb -aa:2 xyz #args: uvw xyz #options: aa=[1,2] bb=true
In this example, the aa
option has two values, stored in an array. The bb
is specified without value, and is assumed true
.
When Judo starts, the JVM has an initial classpath called system classpath. Judo also allows additional user classpaths. This is done through the predefined constant, #classpath
object.
To add a user classpath component, which can be either a file system directory name or a zip or jar file, use the add()
method of #classpath
, which can add individual class path name or an array of them. When Judo tries to load a Java class or a resource, the classpaths are searched first; if the class or resource is not found, Judo tries the system classpath. The following program demonstrates how to use this object:
Listing 4.2 add_classpath.judo |
---|
println '---- Just system classpath ----', nl, #classpath; #classpath.add('c:/temp'); println '---- Added user classpaths ----', nl, #classpath; a = new java::alfa; println nl, a; |
The toString() method of #classpath
prints out the complete classpath at the time of execution, where user classpaths are displayed first. The alfa
is a test class put in the c:\test
. You can inspect classpath components stored in #classpath
like this:
Listing 4.3 list_classpath.judo |
---|
#classpath.add('c:/temp'); println '---- User classpath components ----'; for x in #classpath.userClasspaths { println x; } println nl, '---- System classpath components ----'; for x in #classpath.systemClasspaths { println x; } |
To add an array of class path names, such the jar or zip files under some lib
directory, do this:
Listing 4.4 add_libjar_2cp.judo |
---|
/* * To add all the jar/zip files in ${deploy}/lib and ${thirdparty}/lib * into the (user) classpath. */ arr = []; listFiles <arr> '*.jar, *.zip' in '${deploy}/lib'; listFiles <arr> '*.jar, *.zip' in '${thirdparty}/lib'; #classpath.add(arr); |
Java classes can be loaded into constants in Judo. Keep in mind that user classpaths are added at runtime, where constants are set at compile time. So the following code would fail:
#classpath.add('c:/temp'); const #CDimension = java::java.awt.Dimension; // OK const #CAlfa = java::alfa; // fails, even though alfa.class in in c:/temp!
Java classes in the system classpath are ok.
As you may well know, when you run any Java software, the JVM contains a set of system properties. In the class java.lang.System
, its static method getProperties()
returns a java.util.Properties
, which is essentially a name-value pair map, that includes all the system properties. You can specify runtime attributes on the command-line like this:
java -Dabc=1 -Dxyz=aaa judo sysprops.judo
User defined properties are also part of the java.util.Properties
returned by System.getProperties()
method. The JVM predefines a list of system properties that describes various aspects of the JVM, system and runtime information. The following table lists what is defined in Java version 1.4:
Key | Description of Associated Value |
---|---|
java.version | Java Runtime Environment version |
java.vendor | Java Runtime Environment vendor |
java.vendor.url | Java vendor URL |
java.home | Java installation directory |
java.vm.specification.version | Java Virtual Machine specification version |
java.vm.specification.vendor | Java Virtual Machine specification vendor |
java.vm.specification.name | Java Virtual Machine specification name |
java.vm.version Java | Virtual Machine implementation version |
java.vm.vendor Java | Virtual Machine implementation vendor |
java.vm.name Java | Virtual Machine implementation name |
java.specification.version | Java Runtime Environment specification version |
java.specification.vendor | Java Runtime Environment specification vendor |
java.specification.name | Java Runtime Environment specification name |
java.class.version | Java class format version number |
java.class.path | Java class path |
java.library.path | List of paths to search when loading libraries |
java.io.tmpdir | Default temp file path |
java.compiler | Name of JIT compiler to use |
java.ext.dirs | Path of extension directory or directories |
os.name | Operating system name |
os.arch | Operating system architecture |
os.version | Operating system version |
file.separator | File separator ("/" on UNIX) |
path.separator | Path separator (":" on UNIX) |
line.separator | Line separator ("\n" on UNIX) |
user.name | User's account name |
user.home | User's home directory |
user.dir | User's current working directory |
Judo's built-in constant, #sysprops
, refers to the system properties.
JVM system properties are different from environment variables. The environment variables are for the operating system process that runs the current JVM. In Judo, there are two system functions, getenvs()
and getenv(name)
. Alternatively, you can use the ${name}
notation in place of getenv(name)
; ${name}
can appear within string literals as well; see String and Character for details. Let's look at an example.
Listing 4.5 sysprops.judo |
---|
// Print out system class path: cp = systemProperty('java.class.path').csv(${:}); for x in cp { // print class path nicely. if loopIndex() == 0 { println 'Java class path: ', x; } else { println ' ', x; } } // Print out a few pieces of system information: println 'OS Name: ', osName(); println 'isMac: ', isMac(); println 'isWindows: ', isWindows(); println 'isHP: ', isHP(); println 'isLinux: ', isLinux(); println 'isSunOS: ', isSunOS(); println 'javaVendor: ', javaVendor(); println; println 'fileSep: ', ${/}; println 'pathSep: ', ${:}; println 'homeDir: ', ${~}; println 'curDir: ', ${.}; println "cd '/'"; cd '/'; // change curDir(). println 'curDir: ', ${.}; println 'userName: ', #user; // Print out all the system properties: println nl, '=======================', nl, ' All system properties', nl, '======================='; for k in #sysprops { println k:>26, ' = ', #sysprops.(k); } // Print out all the environment variables of this JVM process: println nl, '==========================', nl, ' All environment variables', nl, '=========================='; envvars = getenvs(); for k in envvars { println k:>26, ' = ', envvars.(k); } |
In this program, it first prints out the JVM's class path; then it prints out some system information. The osName()
, javaVendor()
and the isXXX()
functions are all Judo system functions that obtain information from the system properties. Finally, it prints out all the system properties and environment variables.
Judo programs can include other scripts with the !include
clause like this:
!include 'common.judi'
By convention, scripts intended to be included have their file names end with .judi
. The included files texturally become a part of the current script. Usually included files are used for libraries of functions, user-defined classes, constants, and sometimes global variables or initialization/clearn-up code. Generally, it is not a good idea to put a lot of executable code in included files because this may lead to subtle bugs that are hard to figure out. This is a style issue.
Conditional include
Files can be conditionally included. The complete syntax for !include
is as follows:
Include | ::= | !include ( if Expr | ifexists | ( ifdef | ifndef ) CONST_NAME )? STRING_LITERAL |
Between !include
and the file path, you can specify a condition; if the condition is an expression, it must be a constant expression. Only when the condition is true
will the file be included. The conditions can be the existence or otherwises of a constant, the existence of the included file itself, or a boolean expression. The following shows some examples of conditional include:
!include if ${LOCALE}==null 'messages_en.judi' !include if ${LOCALE}=='en' 'messages_en.judi' !include if ${LOCALE}=='es' 'messages_es.judi' !include if ${LOCALE}=='sim_cn' 'messages_cn_s.judi' !include if ${LOCALE}=='tra_cn' 'messages_cn_t.judi' !include ifexists 'init.judi' !include ifdef #debug 'debuglib.judi'
The syntax of ${LOCALE}
accesses the environment variable at runtime. For more details, see String and Character.
Environment variables in include file path
The ${}
syntax can also be embedded in string literals. In the case of !include
, ${}
's are only interpreted as environment variables. So you can include files like this:
!include '${ENVROOT}/bonanza/common.judi'
Pragmas are instructions inside program source files to modify the behavior of Judo language system at different levels, such as parsing, script internal states and runtime characteristics. Pragmas take this general syntax:
pragma | ::= | !pragma PRAGMA_NAME = LITERAL |
where the PRAGMA_NAME is a dot (.
) separated name, exactly like a qualified Java class name. Each pragma follows its own rules and has its own use, which will be discussed in various parts of this book.
Collectively, !include
, !pragma
and any other mechanisms in Judo that starts with an exclamation mark (!
) are called language directives, which is a not-so-useful concept.
Judo scripts can be made executable on both Windows and Unix platforms. Let's see how they can be done on Windows and Unix.
On Windows, we create a simple batch file to run Judo:
Listing 4.6 judo.bat |
---|
java judo -q %1 %2 %3 %4 %5 %6 %7 %8 %9 |
This script assumes that all the necessary Java software libraries, including judo.jar
, are in the CLASSPATH
environment variable. Then, you can run Judo scripts like this:
judo myscript.judo param1 param2
Next, use the Windows Explorer to associate this batch file to files with extensions of .judo
, .jud
, .judi
and any extensions you like to use for Judo scripts. To do this, first start Windows Explorer and navigate to a file of one of those extensions; right-click on that, and choose "Open With" and then "Choose Program". The "Open With" dialog box shows up; check the box of "Always use this program to open these files"; click on the "Other" button, navigate to the judo.bat
you have created, and finish. Next time, you can run a Judo script by either double-clicking on a Judo script in the Windows Explorer, or directly type the script name in a console window (DOS box).
It is easy to create an executable script on Unix to run Judo:
Listing 4.7 /usr/local/bin/judo |
---|
java judo $* |
Certainly, don't forget to chmod
it to be executable. So you can run Judo on the command line like this:
judo myscript.jdo param1 param2
To make the Judo scripts auto-run, put the following as the first line in the script:
#!/usr/bin/env judo
And don't forget to chmod
the Judo script to be executable. It seems that the following should also work:
#!/usr/local/bin/judo
but there are reports that the first approach works better in most shells, and this is not Judo-specific.
Internally, the Judo engine logs various messages. These messages are classified as trace, debug, info, warning, error and fatal; they are collectively called logging levels. A program can programmatically change the logging level via !pragma
mechanism.
Judo has these logging sources within the language engine:
Logger Name | Description |
---|---|
judo | The general logger for the whole language engine. |
judo.user | The user log writable via the print <log> statement. |
judo.jdbc | The JDBC logger. This logger is set to java.sql.DriverManager via a call to its setLogWriter() method. |
judo.hibernate | The Judo logger for its Hibernate scripting events. |
judo.schedule | The logger for the schedule statement. |
When the language engine starts, all the loggers are initialized to a same logging level, depending on the configuration. Logging levels for any of these sources can be modified via the logging pragma like this:
!pragma logger.judo = 'warn' !pragma logger.judo.user = 'trace' !pragma logger.judo.jdbc = 'debug' !pragma logger.judo.hibernate = 'info'
The logging levels have priorities, from lowest to highest in this order: trace
, debug
, info
, warn
, error
and fatal
. The lower the priority, the more verbose. For instance, if a logger is set to warn
level, then all warning, error and fatal messages will be logged. You can also specify an all
level, which equates to trace
. You can also set an off
level to turn off logging altogether.
A special case is to set the logging level for judo.user
; you can use logger
without suffix to represent judo.user
:
!pragma logger = 'trace'
There may be Java software packages used with your Judo programs. A classical example is Hibernate. These packages often log various messages from different parts.
Logging is a basic part of any software package. Java software can choose their own way of logging, such as writing to System.err
or System.out
, a file, operating system system logs or even databases. In modern Java, there are a few common choices, notably the JDK 1.4 logging API and the Apache Log4J logging framework. In addition, there is a light-weight wrapper API called Apache commons-logging
package that provides a common API to various logging implementations. This is what is used by Judo, Hibernate and many other open source and commercial software products and projects. We need to discuss this package in a little more detail.
Apache commons-logging package
As just mentioned, the Apache commons-logging package is a wrapper on top of various logging APIs. It is closely modeled after Log4J, and provides a SimpleLog
of its own in case no other logging implementation is present. By default commons-logging picks a logger with this discovery strategy:
org.apache.commons.logging.impl.SimpleLog
.If the non-Judo software package does use commons-logging API, you can set the logging level for them as well: simply append the logger name to logger.
in the pragma:
!pragma logger.org.hibernate = 'warn' !pragma logger.org.hibernate.SQL = 'warn'
The commons-logging API has no special requirements for configuration; the logging configuration must be provided for the used logging mechanism. For instance, Log4J takes a log4j.properties
file in the root of the classpath (among other means), and JDK 1.4 logging API reads a logging.properties
at start, also from the root of the classpath.
Judo installation provides a default log4j.properties
and logging.properties
. They are not in the classpath automatically. Take a look at them if you intend to use, and edit them based on your needs, such as setting the logging levels, logging destinations and/or mechanism. The details of these two logging APIs are beyond the scope of this book, and you can find a lot of resources on them both online and in books.
Judo has a special mechanism for structured documentation of programs, the usage
block at the beginning of scripts. Its contents are key-value pairs, where the values are any literals. There are three particular keys, minArgs
, args
and desc
. The key minArgs
must take an integer number; if it is specified and the number of arguments is less than that, Judo prints out args
and desc
. The args
can be any format and does not serve any other particular purposes other than displaying. The following is an example:
Listing 4.8 usage.judo |
---|
usage { desc = [[* This program displays a text file of any encoding in a simple Java GUI. It is easy to use. Simply provide a text file name and optionally an encoding name; the default is your current Java default encoding. *]]; minArgs = 1; args = 'file [encoding]'; author = 'James Huang'; created = '2002-11-14'; lastMod = '2004-06-28'; } |
When run it without any parameters, it prints out the help message like this:
% java judo usage.judo Usage: java judo usage.judo file [encoding] This program displays a text file of any encoding in a simple Java GUI. It is easy to use. Simply provide a text file name and optionally an encoding name; the default is your current Java default encoding.
The special options, -?
and --helper
, tell the Judo engine to print out the same help message and exist the script.
usage
block can appear only once in a script. If the script includes other scripts, only the main script's usage
is used.