Java as a library Part 1

In the Unix/Linux world there is this utility called ‘grep’. It is a very handy tool that allows you to search for a string of text in one or more files of a directory. The original grep can handle wildcards in file names and regular expressions in the target text. My goal is to experiment with the Java Native Interface (JNI) between ALGOL and Java and to do so, I will implement a simplified version of grep. The simple version of grep still shows the power of MCP Java. Java is used to search for file names in directories and to scan the files for the target text. Using Java to find a target text in a string is all standard stuff, but I’m especially pleased with the Java ‘listFiles’ method, which is so much easier to use than setting up GETSTATUS calls from ALGOL.

I have set up a Netbeans project named ‘grep’. The class I am going to call from ALGOL is Grep. This class contains one public method, doGrep. The sourec code for the Grep class is here.

Only the method doGrep has been declared public. JifGen will generate an ALGOL call for this public method.

I am catching all exceptions in the Java program, because I have not yet quite figured out how to recover from all sorts of Java exceptions on the ALGOL end. It seems that simply re-initializing the JVM is not good enough. I suppose this can be the subject of another blog post.

To test the Grep class in my MCP environment, I included a test driver class, Greptest, in the project. GrepTest just contains a main method that calls Grep.doGrep(). I can now test and debug the Grep class while it runs on the MCP and actually reads MCP files and directories. To easily start the tests in debug mode, I made a small WFL with all the necessary parameters:

NEXT+   ....*....1....*....2....*....3....*....4....*....5....*....6....*....7..
00000100BEGIN JOB DEBUG;                                                        
00000200                                                                        
00000300RUN *DIR/JRE6/BIN/JAVA                                                  
00000420    ("  -Xdebug -Xrunjdwp:transport=dt_socket,address=9000,"            
00000440    &"server=y,suspend=y"                                               
00000490    &" -jar grep.jar -r  grep /-/user/usercode/ruud/grep/src");         
00000500    CURRENTDIRECTORY = "/-/USER/USERCODE/RUUD/GREP";                    
00000600                                                                        
00000700END JOB                                                                 

The arguments “-r grep /-/user/usercode/ruud/grep/src” tell grep to recursively look through the files in directory (RUUD)GREP/SRC/= ON USER searching for the text “grep”. The output of this run is below:

/-/user/usercode/ruud/grep/src/java/Grep.java:package com.mcpjava.mcp.grep;
/-/user/usercode/ruud/grep/src/java/Grep.java:                hits = grep(args[0].trim(), args[1].trim(), false);
/-/user/usercode/ruud/grep/src/java/Grep.java:                    hits = grep(args[1].trim(), args[2].trim(), true);
/-/user/usercode/ruud/grep/src/java/Grep.java:                    hits = grep(args[1].trim(), args[2].trim(), false);
/-/user/usercode/ruud/grep/src/java/Grep.java:    private static ArrayList grep(String text, String path, boolean recurse) {
/-/user/usercode/ruud/grep/src/java/Grep.java:                        ArrayList rslt = grep(text, f.getAbsolutePath(), recurse);
/-/user/usercode/ruud/grep/src/java/GrepTest.java:package com.mcpjava.mcp.grep;
/-/USER/USERCODE/RUUD/GREP/SRC/INCLUDE/GREP_INCL:11700:% com.mcpjava.mcp.grep.Grep
/-/USER/USERCODE/RUUD/GREP/SRC/INCLUDE/GREP_INCL:12200:  80"com/mcpjava/mcp/grep/Grep" 48"00"
/-/USER/USERCODE/RUUD/GREP/SRC/INCLUDE/GREP_INCL:13100:  % com/mcpjava/mcp/grep/Grep
/-/USER/USERCODE/RUUD/GREP/SRC/INCLUDE/GREP_INCL:13500:% ALGOL interfaces for class:com.mcpjava.mcp.grep.Grep
/-/USER/USERCODE/RUUD/GREP/SRC/INCLUDE/GREP_INCL:13900:% Class:       com_mcpjava_mcp_grep_Grep
/-/USER/USERCODE/RUUD/GREP/SRC/INCLUDE/GREP_INCL:18600:% Class:       com_mcpjava_mcp_grep_Grep
/-/USER/USERCODE/RUUD/GREP/SRC/GREPDRIVER:200:% grep
/-/USER/USERCODE/RUUD/GREP/SRC/GREPDRIVER:11000:procedure grep(arguments);
/-/USER/USERCODE/RUUD/GREP/SRC/GREPDRIVER:16520:    ebcdic value array jvmOptions ("-cp ./grep.jar" 48"00");
/-/USER/USERCODE/RUUD/GREP/SRC/GREPDRIVER:18000:end of grep.

Once I’m satisfied with the Grep class, I can generate the ALGOL interface. On my development PC I have a folder named \libs\mcp in which I put all the jar files that are part of the MCP Java release. The JifGen utility is included in the mcptools.jar file. A Netbeans “Clean and Build” creates a jar file in the “dist” folder of the project. The jar file, by default, has the name of the Netbeans project, in my case “grep.jar”. I directed the Command Prompt window to the Netbeans directory for the grep project and laboriously typed in the following command to generate the ALGOL include file:

java -cp /libs/mcp/mcp.jar;/libs/mcp/mcptools.jar;/"program files"/java/jdk1.6.0_24/lib/tools.jar com.unisys.mcp.tools.JifGen -algol -classpath dist/grep.jar -d incldir -output GREP_ -verbose com.mcpjava.mcp.grep.Grep

JifGen has now generated an ALGOL include file named GREP_INCL.ALG_M in directory incldir. An ALGOL procedure has been genereated for each public method in Java class Grep. It generated a procedure DOUBLE PROCEDURE GREP__GREP_DO_GREP which reflects the method Grep.doGrep on the Java side. The default class constructor procedure, DOUBLE PROCEDURE GREP__GREP_GREP, is also generated, but since my callable method is declared static, I don’t need to construct a Grep object.
On my first attempt I had left the “”program files”/java/jdk1.6.0_24/lib/tools.jar” out. This resulted in a Java Exception:
Exception in thread "main" java.lang.NoClassDefFoundError: com/sun/javadoc/Doclet
I reread the Unisys JifGen Javadoc and yes, it’s documented there; apart from mcp.jar and mcptools.jar you also need to include the Java SDK (Oracle) provided tools.jar (that is where the com.sun.javadoc.Doclet is located).
Initially, I also had some trouble getting my classpaths right, but you have to realize that the
-cp /libs/mcp/mcp.jar;/libs/mcp/mcptools.jar;/"program files"/java/jdk1.6.0_24/lib/tools.jar
is to get JifGen going and the
-classpath dist/grep.jar
near the end is to tell JifGen where it can find the classes it must generate ALGOL for. JifGen does not need to see the Java sources because it uses Java Reflection to get the info from the compiled classes. This is the generated GREP_INCL.ALG_M.

In the previous blog I mentioned that the generated file needed an addition to call JNI_CANCEL_PARAMETER_LIST in case of an exception. No change needed anymore, in the latest MCPJava release JifGen has already been fixed!
In the next post I will write an ALGOL program that takes the string parameter, calls the Java’s Grep.doGrep() method with the parameter and unravels the result.

Leave a Reply