SUSE Conversations


ECMAScript/JavaScript Development Without a Web Browser



By: ab

November 12, 2009 4:10 pm

Reads:571

Comments:0

Rating:0

Have you ever been coding Java and suddenly wanted to do something using loosely-typed variables or worked out an issue with a little less Java-ness? Have you ever wanted to take advantage of the familiarity people have with some languages (ECMAScript/JavaScript) while still using something that is not a web browser as an environment? Have you ever wanted to debug your JavaScript without refreshing a web page and your cache that just won’t seem to ever refresh properly for you? This and more will be covered today with practical examples in both the Novell Identity Manager (IDM) and Novell Sentinel applications plus any other Java-based applications that you may have around your own environment.

I think it may be prudent to review briefly what everything we’ll be discussing really is. For example, the title mentions ECMAScript and JavaScript together almost as if they are the same thing; that is actually a nice little coincidence as they are the same thing. ECMAScript is the proper name for the language standard as approved by (you guessed it) ECMA but JavaScript is the common name of the language that we all know and love. JavaScript has the word ‘Java’ in it but another common clarification is that JavaScript is not, in any way, directly related to Java. It is not meant to be a way to script Java and has no ties to the Java language with regard to data types, interpreters, etc. That it is very similar to Java syntactically is completely unrelated to its name; it is as much similar to C++, PHP or other C-ish languages. So as a recap ECMAScript is JavaScript and neither is related to the language and binaries from Sun known as ‘Java’.

Another notable term used throughout this document will be ‘Rhino’ which does not refer to the large mammal with horns on its head but rather to the Mozilla project which makes a JavaScript interpreter available within a Java environment. At some point later I’ll also mention Eclipse, the open source Integrated Development Environment (IDE) originally from IBM. Finally the ‘Aptana’ plugins for Eclipse make the IDE a bit more friendly. They are not required for anything specifically to work but they are recommended when doing Sentinel SDK development which is largely based on JavaScript. All of these tools and technologies are (in some form or another) free for use and most if not all are also open source.

I’m using a few examples from other sites I found online in preparing this which you may want to review for second opinions and clarifications on the topics covered herein. I’ll try to mention them as I borrow from them but they include W3Schools, Jo Edkins’ JavaScript tutorial, and the Rhino project page. Later as we get into IDM and Sentinel-specific tasks we’ll be doing things that may reference the Sentinel SDK specifically.

In the introduction I mentioned a few reasons for going into browser-less JavaScript. Despite my disclaimer earlier that JavaScript and Java are not related (that is true: they are not) they can be used together thanks to the project known as Rhino. Rhino comes in the form of a JAR file which can be added to a Java application’s classpath statement. Once there then the ability to run JavaScript code within Java (with some limitations) is present and makes up the rest of this document. There are some notable differences between JavaScript in a browser and JavaScript within Rhino that will be obvious to anybody who has used JavaScript before.

First, there is no Document Object Model (DOM) with which to interact. For example, ‘document.write(“stuff”);’ completely fails because there is (by default) no ‘document’ object. Other little debugging tricks like using ‘alert’ also fail. On the good side, though, there is the ability to run JavaScript interactively which can be nice for quick development and immediate feedback on simple problems with syntax or other runtime issues (invalid objects, methods, etc.). Instead of adding an alert statement a developer can simply use the print() method and put the data to be printed in there and see it without refreshing a browser or saving a file. I also mentioned the possibility of using what is essentially becoming a commodity programming language; everybody who has done basic web development has come across JavaScript and likely written a little bit. The syntax is familiar and examples of ways to do many things are plentiful. JavaScript is at the heart of AJAX and large frameworks such as the Google Web Toolkit (GWT). Needing to hire a ten-year-veteran of a proprietary programming language is not necessary for simple development work.

Let’s dive right in. I’ve debated putting the JavaScript stuff before/after the Rhino stuff and have decided to put JavaScript first as that is what this is primarily about. If you want to get the Rhino side of things setup it is trivial and will be covered below so jump down, get that, then come back to walk through everything on your own computer as you read it. I will be separating out code examples so it should be easy to read this then setup the environment, and just run code like crazy later on. First the basics of JavaScript are similar to other programming languages you may have used. There are operators similar to those you have probably used before, variables, conditionals, loops, and JavaScript is also object-oriented. Unlike Java and some other languages the variables can change data types or be treated as different data types very easily. In the case of the Rhino implementation of JavaScript any classes available to the Java environment running Rhino are also available within JavaScript as will be shown later.

Variable declaration takes place with a string representing the variable which is made up of alphanumeric characters plus underscores (A-Za-z0-9_) and variables must start with a non-numeric from what I’ve found. Unlike some other languages like Perl or PHP there is no character preceding a variable to mark it as a variable. A variable has a scope that can be either limited by using the ‘var’ keyword or it can be a globally-scoped variable by simply declaring and instantiating the variable without the ‘var’ keyword. Declaring a variable just means telling the interpreter that the variable exists; initializing the variable means not only telling the interpreter that the variable exists but that it should also have a value which is then set into the variable. Declaring a scoped variable of ‘myusername’ and initializing it to have the value ‘ab’ all in one command would look like the following:

var myUserName = 'ab';   //Declare 'myUserName' and set it to the string value 'ab'.

var myAge = 30;    //Declare the 'myAge' variable and initialize it to the numeric value 30.

var isCool = true;    //Declare and initialize isCool to the boolean value true.

Note in the examples above that the first one included single-quotes around the value ‘ab’ while the second did not have quotes around numerics. Like many languages quotation marks should be used around strings but are not needed for numeric or boolean data types. Something else potentially of note is the use of comments at the end of the line. Like in Java, C and other languages single-line comments start with two slashes (sometimes known redundantly as forward-slashes) and comment out the line from the point of the two slashes until the next line starts. Multi-line variables are also similar to C where they start with /* and end with */ and everything in between is a comment. Also note the use of whitespace. Between keywords and variables (‘var’ and ‘myUserName’) whitespace must exist, but around the equal sign and between the semicolon and the comments the whitespace is optional and just there for readability. This bring up another point: newlines are not there to end a line or a statement in most cases as semicolons do that. In the example above newlines were needed to end the comments but otherwise the three variable initializations could look like the following and have exactly the same outcome:

var myUserName='ab';var myAge=30;var isCool=true;

Moving along it is possible to declare but not initialize a variable by simply using the ‘var’ keyword with the variable name (or names) and then a semicolon to end the line. The three variables above could be declared in the following way though they would all be null:

var myUserName, myAge, isCool;    //Declare but do not initialize explicitly.

In many languages declaring a variable without initializing it sets the value to a null value or a similar default and JavaScript is no exception to that convention. Because I was taught to always initialize variables when declaring them (to keep from getting frustrated when using languages that do not automatically initialize variables for you, such as C++) all of the examples here will declare and initialize variables and I encourage that behavior as it is a good practice overall.

So we have variables, let’s do a little bit with them (I recommend having them set for these examples as it is more fun than using uninitialized variables). First, let’s do the basic printing stuff:

print('Hello World!');   //Basic Hello World program... a one-line in JavaScript; doing this in a browser would simply replace the 'print' section with 'alert'.
print(myUserName);  //Print whatever is currently in the myUserName variable.  Notice there are no quotes around the characters that make up the variable.
print(myAge);        //Print '30' to the screen.
print(myUserName + ' ' + myAge);    //Print 'ab 30' by concatenating myUserName's value with a single space and then with myAge's value.
print('It is a true statement that ' + myUserName + ', who is age ' + myAge + ', has an isCool variable set to ' + isCool + '.');

Okay we have beat these variables and printing commands to death and it all seems to work. Let’s play with operators besides the assignment operator (also known as the equals sign). So far we have just set variables to a value and that is it. It is time to change the values. You have already seen that ‘+’ can join strings (concatenate) together but it is also available for arithmetic when numeric types are used exclusively:

myAge = myAge + 3;   //myAge should now be 33 if it was 30 before
myAge += 3;    //Functionally equivalent to the line above, but shorter.  myAge is now 36.
myAge /= 12;   //Divide myAge by twelve and set the result back into myAge... should be 3 now.
myAge *= 12;   //Multiply myAge by twelve and set the result back into myAge... should be 36 again.
--myAge;   //Decrement the value of myAge by one and set it then returning the value (35);
myAge--;   //Return the value of myAge and THEN decrement it by one so the NEXT time myAge is called it will show the value of 34.
myAge++;   //Increment the value of myAge after returning the current value (34).
++myAge;   //Increment the value of myAge and return the value (36);
myAge = 0XFF;   //Set myAge equal to 0XFF which is a hexadecimal representation of the decimal number we all know and love as 255;
myAge = 3.00e2;   //Use scientific notation to set myAge to 300.

Most of these examples apply to just about any programming language you can find so let’s move over to Arrays. An array is a set of multiple values all available via a single variable name. The different values are accessed individually by using an index or an offset from the variable name itself. For example the zeroth offset [0] from ‘myFavoriteThings’ will be ‘pianos’ while the first offset [1] will be books. See below for the code examples:

var myFavoriteThings = ['piano', 'book'];   //Initialize the myFavoriteThings array with two values.
var myFavoriteThings = new Array('piano', 'book');    //Functionally equivalent to the previous initialization of the array.
print(myFavoriteThings[0]);   //Print 'piano'
print(myFavoriteThings[1]);   //Print 'book'
print(myFavoriteThings[2]);   //Print 'undefined' since this index does not reference anything that is as of yet defined in the array.

Something nice about a few of these languages is that Arrays are defined as ‘sparse’ arrays meaning that they only allocate memory for elements which have data in them. For example you could define an array with ninety-nine slots (places, indexes, offsets, whatever) for strings but only those indexes which were populated would actually take up space. The arrays in JavaScript (and other languages too) are also variable in size allowing you to add or remove elements on the fly without redefining the array’s size in a static way which is very nice.

Another note that always makes me happy is that JavaScript arrays are naturally ‘associative’ arrays which means that the index does not always need to be just a simple integer but can be a ‘key’ which then has a ‘value’ referred-to by the key. Initializing an associative array is a little different but still simple to understand; notice the use of the braces around the key/value pairs instead of brackets around the values alone:

var myFavoriteThings = {'instrument':'piano', 'media':'book'};
print(myFavoriteThings['instrument']);   //Prints 'piano'.
print(myFavoriteThings['media']);   //Prints 'books'.

The initialization sets up the associative array (Perl programmers will recognize the term ‘hash’ meaning the same thing) of myFavoriteThings with a key of ‘instrument’ which refers to the value ‘piano’ and the key ‘media’ which refers to the value ‘book’. Just to mention a quick example about multi-dimensional arrays JavaScript supports these as well and the syntax just nests what we have already learned to provide the functionality. This gets confusing for beginners so touching on this is probably all I’ll do:

var myPets = {'cats':['rosco', 'tiger'], 'horses':['mikey', 'shasta']};    //Initialize the myPets associative array with a 'cats' key referring to an array of names of cats, and a 'horses' key referring to an array of names of horses.
print(myPets['cats'][0]);    //Prints 'rosco';
print(myPets['horses'][1]);     //Prints 'shasta';

With the basics of variables, arrays, and operators under our belt we can start doing fun things like conditionals and loops. The basic conditional executes code based on conditions that are checked. For example writing code to tease somebody for being “old” is trivial:

if(myAge > 29) {    //Check for the variable 'myAge' being greater-than the numeric value 29.
  print('Oh boy you sure are old....');    //Print a message
} else if (myAge > 25) {
  print('The joys of middle age... hopefully you are no longer dumb and acting like it but are still not too old either.');    //If the previous if statements haven't matched try this one.
} else {    //If the previous if statements did not match then the 'else' branch is used instead, assuming an else branch exists that is....
  print('Whippersnapper!  Get off my lawn.');
}

For a lot of possibilities for a single ‘if’ statement sometimes a ‘switch’ or ‘case’ statement is advantageous.

myResponse = 'optimistic';
switch(myResponse) {
  case 'pessimistic':
    //do something here for pessimistic responses
    break;   //skip to the end
  case 'optimistic':
    //do something here for optimistic responses
    break;   //skip to the end
  default:
    //Do whatever you want here for those times the previous cases were not met.
    break;
}

After conditionals are understood loops seem like a good place to venture next. We’ll go over the ‘for’ and ‘while’ loops briefly. The ‘for’ loop starts out and typically sets an initial value for a counter/iterator, then sets a condition for how long the loop should run, and finally ends with a statement about how to increment the counter or iterator. All of the code within the braces is then run as long as the condition within the definition of the ‘for’ loop is true:

//Setup ctr0 to the value 0 and loop while ctr0 is less-than 10 incrementing it by one (++ctr0) at the start of each iteration.
var factorial = 1;
for(var ctr0 = 0; ctr0 < 10; ++ctr0) {
  print(ctr0);    //Print the current version of the ctr0 variable
  if(ctr0 != 0) {
    factorial *= ctr0;
  }
}
print(factorial);

The loop above combines a loop and a conditional as well. It initializes ctr0 to 0 and then loops while that variable is less-than 10, incrementing it by one every iteration. Inside the loop it prints the value of ctr0 and also, if the ctr0 value is not equal to 0, multiples it by the current ‘factorial’ variable (which was originally initialized to 1) and after the entire loop is completed prints out the factorial. This functionally is a little script that, with a lot of extra stuff, gives us the value for 9! or nine factorial (the product of all integers from one to nine) or 362880. Another kind of loop is the while loop which leaves control of counters or iterators to you and just loops while the user-defined condition at the beginning is true:

//Another factorial generator, though this one stops when the product reaches a certain value rather than when the factors reach a certain value.
var ctr0 = 1;
var factorial = 1;
while(factorial < 320000) {
  factorial *= ctr0;
  ++ctr0;
}
print(factorial);

Another type of loop that is very handy is one that loops through values of an object. Remember those arrays? What if you wanted to print everything in an array? Turns out that is also quite easy:

//Print all of the pet types.
for(var onePet in myPets) {
  print(onePet);
}

Note that if you still have the multi-dimensional array setup from above this only prints the keys for the first array and does not go into the nested arrays or other data structures. This is typically useful as you could easily get into those now by nesting another loop within the first loop that went through all of the values for any object found during this loop’s iteration.

//Print all of the pet types and then the pets.
for(var firstElement in myPets) {    //Loop through all of the types of pets as those are the keys for the myPets associative array (hash).
  print(firstElement);   //Print this type of pet.
  for(var secondElement in myPets[firstElement]) {    //Loop through the keys of the nested arrays of the top arrays to get the names of the type of pet through which we are currently looping.
    print('  ' + myPets[firstElement][secondElement]);    //Print a couple spaces and then the name of the current pet.
  }
}

So with very little code we can get a list of everything within nested associative arrays.

After about ten minutes of programming (if that) you will probably find that you do the same things over and over. For example you probably print a lot, you may want to handle data types or objects in certain ways, or whatever. In programming exists a concept of functions or methods which take parameters (typically) and return a value or some output after doing a specific bit of processing on the available data. This is useful as it encourages data reuse so you are not writing the same ten lines of code thousands of times in a program or script and then having to modify all of those thousands of instances the second you determine that you have a glitch in your logic or a change to what your code does because of new requirements. Creating a method (function) in JavaScript is easy and it acts a lot like a variable. A method has a keyword (‘function’) followed by a name (myFunction) and then data which are used within the method. In the case of the method the code is also executed when the method is called but otherwise methods are very similar to variables:

//Define a method named addTwoNums which, when sent two values, adds them together and returns the result.
function addTwoNums(num0, num1) {
  return num0 + num1;
}

The sample method above could be called like the following, which I expect would end up printing the value 25 to the screen:

print(addTwoNums(18, 7));   //Print the sum of 18 and 7 to the screen.
print(addTwoNums(12345, 678910));   //Print the sum of '12345' and '678910' which is 691255.
print(addTwoNums(myUserName + ' has isCool set to ', isCool));   //Concatenates because of loose interpretation of data types in JavaScript and the overloaded '+' operator which also concatenates data.

Another benefit of using methods/functions is you can describe complicated code in a simple fashion. As an example it is easier to name a method something about returning a random integer from zero to four billion and change than it is to figure out what the code does sometimes:

//Method to get a random integer from zero to 2^32.
function getRandomInteger() {
  return (Math.floor((Math.random()*(Math.pow(2, 32)))));
}

print(getRandomInteger());

Finally this is a nice introduction to objects. In the previous example I made three calls to something called ‘Math’ which is a math object in JavaScript that is just waiting to be used for fun things that otherwise are not always fun to code. Specifically I used Math.floor() (round to the next integer below the value passed into the method), Math.random() (returns a decimal number between zero and one) and Math.pow() which is used for getting the results of one number taken to some power (two to the third power, for example, is eight). The Math object also returns results for things like pi (3.14159…) and other numbers that are fairly constant in the universe. It includes ways to calculate the sine, cosine, and other geometric things. Objects, in the software sense, are instances of certain classes and therefore take on the attributes and methods of those classes. The Math object exists to provide calculated results to the programmer as shown in the previous example. In JavaScript it is possible to create classes for your own purposes. A common class example is the ‘Person’ class. Instance variables often include things like eye color, first name, government identification number, e-mail address, etc. Instantiating an object named ‘johnSmith’ of the Person class could then have the variables set with data specific to John Smith. Benefits of doing so mean the ability to contain all of the attributes related to a single object all in one place instead of trying to maintain different arrays of the data for the object.

One thing that is interesting about variables in JavaScript is that you do not need to declare them as a specific object type (class) all of the time for them to be treated like a certain class. As an example any old string variable can immediately use the methods of the String class. Consider the following example where a simple string is created out of thin air but then the method toUpperCase() is usable to return the string all in upper case and the length attribute is usable to return the length of the string::

var myUserName = 'ab';    //Initialize the variable.
print(myUserName.toUpperCase() + ' is ' + myUserName.length + ' characters long.');    //Print a sentence showing the string all in upper case and the length of the string.

Other methods of the String class including the indexOf() method which returns the offset of a given substring. The match() method also lets the programmer test if the string matches a given pattern known as a regular expression. Some examples follow:

print('b' is found in the myUserName variable at index ' + myUserName.indexOf('b'));    //Print a sentence along with the offset of the 'b' character.
print('"b" will be replaced by " qwerty" as a result of this example');
print(myUserName.replace('b', ' qwerty'));   //Replace 'b' with ' qwerty' and print the resulting 'a qwerty' but do not set myUserName to this new value.

As another example of a built-in object we have the Date object which, as you probably guessed, deals with date and time functions. With this we can get the current time as it is set in different timezones around the world, in different formats (two and four-digit years for example), and other different ways. A date object can be set to the current date (and is by default) but can also be set to another arbitrary point in time. By comparing a Date object initialized before an operation and then after the operation is completed the time taken to perform the operation can be ascertained. As an example I’ll create a Date object, count for a while, then create another Date object, and compare them to see the number of milliseconds between them:

var ctr0 = 0;
var myDate0 = new Date();      //Create an object to help timing a counter from zero to some number specified in the for loop.
for(ctr0 = 0; ctr0 < 1000000; ++ctr0) { }
var myDate1 = new Date();      //Create an object at the completion of the counting operation.
print('Counting took ' + (myDate1 - myDate0) + ' milliseconds.');   //Print the time taken in milliseconds.

The resulting text on my laptop was ‘Counting took 139 milliseconds.’ Some other basic calls to the Date object will show month, day, hear, hours, day of the year, seconds since 1970-01-01, etc. One really neat property of JavaScript objects is the ability to convert them back to the source which created them. For example the following examples show the code to create the myDate0 and myDate1 objects above:

print(myDate0.toSource());    //Show the source of the myDate0 object.
print(myDate1.toSource());    //Show the source of the myDate1 object.
print(Date().toSource());    //Show the source of a new Date object as it would be created right now.

The result of the three commands above (for me right now) follows:

(new Date(1257391274428))
(new Date(1257391274567))
(new String("Wed Nov 04 2009 20:31:03 GMT-0700 (MST)"))

One value here is that an object could be saved out to a string and read in by another script or a later incarnation of the same script. It also aids in learning about the objects’ creation as well as seeing how the objects are currently setup. Also remember all of those Daylight Savings Time perils that crop up every year or so, or maybe every few months? Creating a Date object that is powered by Java and therefore knows everything Java knows (as updated by tzupdater) is trivial and seeing what the time is per that Java invocation does not require coding anything in pure Java then compiling and running. Just create a date object and see what the time is relative to GMT, or see how the date object looks in its source to see if the current time is inside the range of dates for daylight savings time (MDT for Mountain Daylight Time) or outside that range (MST for Mountain Standard Time). As any old Java implementation can be used to load the Rhino environment in which we do all of this current stuff checking one Java implementation and then another is trivial.

So now we have a basic overview of the JavaScript language it is probably a good idea to talk about how to do this outside a browser finally. It’s time to show off the Rhino environment in which all of these examples are currently running. In order to get Rhino working on your system one simply needs a Java runtime installed (chances are one is already there, but if not get one from http://java.sun.com/ or your system’s favorite installation source) and then the JAR file from the Rhino project that provides the desired functionality. With Java (including the ‘java’ executable in your user’s PATH) and the JAR in place the following command should get things going for you.

java -jar /path/to/js.jar
Rhino 1.7 release 2 2009 03 22
js>

As you can see the prompt is now changed; here we can type or paste input and have it executed in realtime. As an example of both the input and the output:

js> var ctr0 = 0;
js> var myDate0 = new Date();      //Create an object to help timing a counter from zero to some number specified in the for loop.
js> for(ctr0 = 0; ctr0 < 1000000; ++ctr0) { }
js> var myDate1 = new Date();      //Create an object at the completion of the counting operation.
js> print('Counting took ' + (myDate1 - myDate0) + ' milliseconds.');   //Print the time taken in milliseconds.
Counting took 823 milliseconds.

To get start call the help() method which should help provide some built-in commands that will help over time. I have called it in the example below to give some idea of the output and options available by default besides what was covered above:

js> help()

Command                Description
=======                ===========
help()                 Display usage and help messages.
defineClass(className) Define an extension using the Java class
                       named with the string argument.
                       Uses ScriptableObject.defineClass().
load(["foo.js", ...])  Load JavaScript source files named by
                       string arguments.
loadClass(className)   Load a class named by a string argument.
                       The class must be a script compiled to a
                       class file.
print([expr ...])      Evaluate and print expressions.
quit()                 Quit the shell.
version([number])      Get or set the JavaScript version number.
gc()                   Runs the garbage collector.
spawn(arg)             Evaluate function or script name on a new thread
sync(function)         Creates a synchronized version of the function,
                       where the synchronization object is "this"
readFile(fileName [, encoding])
                       Returns the content of the file as a string.
                       Encoding of the string can be optionally specified.
readUrl(url [, encoding])
                       Similar to readFile, reads the contents of the url.
runCommand(name ...)   Runs a specified shell command. Additional arguments are
                       passed to the command
seal(args ...)         Seals the supplied objects
toint32(arg)           Converts the argument into a 32-bit integer
serialize(obj, fileName)
                      Serializes an object and saves it to a file
deserialize(fileName)  Reconstructs a serialized object
environment            Returns the current environment object
history                Displays the shell command history

Some of the these you should know already, such as print(). Another that will stand out to Java aficionados is gc() which lets you call the Garbage Collector to force memory to be cleaned up. quit() is also useful; you MUST have the () (parentheses) at the end of all of these including quit() in order to invoke the method or else you will simply get the definition (code) of the method back which isn’t that help when trying to close your JS environment. Also included is readURL which goes out and reads the URL given to it and returns whatever it finds there to your screen which could include remote JS code, XML for parsing, or anything else like that. The following command, for example, pulls Google’s homepage HTML into the googleHTML variable:

var googleHTML = readUrl('http://www.google.com/');

The load() method will also read in local files as JS code so you can include functions from other files to keep your files modular and flexible. Also recall that this entire environment is running within Java so we still have access to any classes available via Java as long as the JRE can call them and they are defined within JS. Making other classes available is simply a matter of modifying the calling JRE’s classpath statement to include the paths to relevant classes. The ‘java’ command accepts the ‘-cp’ parameter followed by a colon-delimited list of paths to classes. Once that is done the JS environment must still be told to make those packages available in one way or another. The following are examples that should work assuming that the necessary JAR files are present for the non-native Java classes:

importPackage(java.lang);   //Import the java.lang package so classes within this package are available.

importPackage(Packages.com.novell.xml.util);    //Import the com.novell.xml.util package; notice Packages must lead the actual package when the package itself is not a part of the java.* hierarchy of packages.

Once these are executed the following commands also work taking advantage of native Java within the JavaScript:

var mylong0 = java.lang.Long.parseLong(binstring, 2);
var base64c = new Packages.com.novell.xml.util.Base64Codec();

With that the wide world of Java is instantly available in a scripting fashion. Notice that ‘mylong0′ and ‘base64c’ are declared as one type or another in JavaScript though they refer to specific objects of certain classes in Java. As you have probably noticed the second command in either of the two preceding code blocks are using a class that comes from Novell. The reason for this is that these are part of the Novell Identity Manager set of classes. Originally when I was told how to use Rhino it was in the context of IDM which supports ECMASCript along with the more traditional DirXML/IDM policy and XSLT. A quick little shell script pulls in all of IDM’s basic classes and launches the Rhino shell in one quick step. That script’s code follows:

<code interpreter='/bin/bash' scriptfile='~/bin/dxrhino'>
#Script from development to load in the IDM environment for rhino testing.  Requires
#rlwrap as well.  Takes one value (JavaScript I believe) as a parameter.
PATH=/opt/novell/eDirectory/lib/nds-modules/jre/bin:$PATH

CP=
for i in /opt/novell/eDirectory/lib/dirxml/classes/*.jar
do
    CP=$CP:$i
done
rlwrap java -cp $CP com.novell.soa.script.mozilla.javascript.tools.shell.Main "$@"
</code>

Notice that the PATH and CP variables are set to include a root-based installation of eDirectory’s IDM engine classes. These can be modified to suit your needs. The most-important part is that a valid Java environment is used (which PATH helps with above) and that the JAR files are included (as loaded in via CP above). One other note is that rlwrap is included on that line where Java is called; among possible other things this lets the up-arrow key work properly to move back to previous commands entered in Rhino. If this is not there it is likely that you will experience garbage characters at the JS prompt when trying to use the up arrow to go to previous commands. rlwrap is trivial to add to the system even if it is from source so I would add it to any system that will be used even remotely. My compiled build shows version 0.30 in case that helps.

In a later article I’ll be discussing some in-depth uses of this technology both within the Novell Identity Manager and Novell Sentinel-based products along with other little scripts that can now be easily created using JS and Java. If you have not done so by this point set up what has been discussed and see what potential uses apply to your own environment. The environment is painless to setup and works reliably thanks to Java.

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)

Tags: , ,
Categories: SUSE Linux Enterprise Server, Technical Solutions

Disclaimer: As with everything else at SUSE Conversations, this content is definitely not supported by SUSE (so don't even think of calling Support if you try something and it blows up).  It was contributed by a community member and is published "as is." It seems to have worked for at least one person, and might work for you. But please be sure to test, test, test before you do anything drastic with it.

Comment

RSS