In addition to the substantial integration with Qshell that the Java runtime provides, application development can be done in Java directly from a Qshell environment.
Three Java command-line utilities stand out above all the others in terms of importance: the java utility, the javac utility, and the jar utility.
Java developers on iSeries can provide Java applications with CL command interfaces, Qshell command interfaces, or other server-side, command-line user interfaces. Java applications range from simple stand-alone applications to large and complex applications that run within a Java server environment like Websphere Application Server.
You use the java utility to run stand-alone Java applications. The utility creates a Java runtime environment (including the Qshell terminal), loads a Java application, and runs it.
Here are two versions of the java syntax that start the execution of a Java class:
java [options] class-name [parameters] java -jar [options] jar-file-name [parameters] file
Depending on the version of the java command that you use, different option parameters are available. Table 23.1 shows the more commonly used options.
Option |
Description |
---|---|
-classpath < path > |
Specifically set the CLASSPATH environment variable of the Java runtime environment created. The value of this parameter overrides the current setting of CLASSPATH. |
-cp < path > |
The -cp parameter is short notation for -classpath. |
-D< property = value > |
Set the value of the Java system property named property to value . A Java system property is a global parameter available to all Java application code in the new Java runtime environment. |
-verbose |
Log information that explains when and from where each class is loaded. |
-verbosegc |
Cause the garbage collector to output messages for each garbage collection. |
-opt |
Change the optimization level. |
To run the java utility, the Java executable code (classes) and the CLASSPATH environment variable need to be set up correctly. Setting CLASSPATH correctly requires an understanding of some of the fundamentals of Java application-development and packaging. You must know the packaging of the applications and third-party libraries you are using. A frequent problem when doing Java development involves difficulties with setting the CLASSPATH variable (or parameter) correctly.
A Java class defines one of the most basic units of application development in a Java program. Most commonly, a single Java compilation unit (an ASCII .java file) defines a single java class. After compilation, the class is contained within a .class file. The .class file is the binary representation of the Java class consisting of Java byte-code instructions. The class file is binary, and is compatible with existing Java implementations on any platform.
The Java language also supports units of application development other than the class, and allows more than one of these structures in a single file. For further reading, study Java interfaces, abstract classes, inner classes, and private or protected classes. A good place to start might be the book Thinking in Java by Bruce Eckel. The electronic form of this book is available for free at www.bruceeckel.com. Another alternative might be a book The Java Tutorial by Mary Campione, also available for free, at www.thejavatutorial.com.
As a beginner, put one Java class in each ASCII .java file. Create Java applications using one or more of those java classes. The classes might or might not access third-party library code. Each class definition includes a statement describing the package that contains the class.
The Java package name serves multiple purposes. The package fully qualifies a class name, allowing it to be uniquely distinguished from other classes. The package also identifies and groups one or more Java classes. Java classes inside a package have privileged access to some of the internals of other Java classes in the same package. The package also describes the physical packaging structure of the Java class.
Use a package and put your .java files and the resulting .class files in a directory structure matching the package name. When you refer to the package or the fully qualified class name, use the Java notation for the package name, with dots separating the elements. When you refer to the file or directory name, use Qshell notation for the package name, with slashes separating the elements.
You can deliver Java applications and classes as stand-alone files, or as part of a jar or zip file. (Jar files are created with the jar utility, discussed later in this chapter.) The classes in the jar or zip file must be contained within a directory entry in the archive file that matches the package name.
Java class files and archive files are binary files, so you cannot modify them with an editor. Similarly, you cannot successfully upload or download them with an ASCII-mode file transfer (in FTP, for example).
Use the -D option to set Java system properties that control the behavior of the Java runtime environment. Each Java application usually has a unique set of Java system properties that it uses to control its behavior.
The iSeries JVM (Java Virtual Machine) uses Java system properties to affect its internal behavior. Table 23.2 describes some of the more commonly used system properties.
Use this property to modify the behavior of Runtime.exec().
Properties |
Description |
---|---|
-Djava.version=1.2 |
Set the version of the Java runtime environment to 1.2. The valid values of this property depend on which JDK versions are currently installed on the iSeries. |
-Dos400.stdin= value |
Assign stdin to a specific file or TCP/IP connection. For example, use file:/path/file.txt, port::21, or port::21 to write data to file.txt or an Internet host at port 21. |
-Dos400. stdout = value |
Assign stderr to a specific file or TCP/IP connection. For example, use file:/path/file.txt, port::21, or port::21 to write data to file.txt or an Internet host at port 21. |
-Dos400.stderr= value |
Assign stdout to a specific file or TCP/IP connection. For example, use file:/path/file.txt, port::21, or port::21 to write data to file.txt or an Internet host at port 21. |
-Dos400. stdio.convert= value |
Use this property to modify the data conversion that is performed on the stdin, stdout, and stderr file streams. The default value is N , no conversion. Other valid values are as follows :
|
-Dos400. runtime. exec = value |
Note |
The behavior of Runtime.exec() changed between iSeries Java versions 1.2 and 1.3. Set this property to a value of EXEC to get the version 1.3 behavior, which executes a program or utility directly. Use a value of QSHELL to get the old behavior (1.2 and prior), which allows Runtime.exec() to execute a Qshell command. |
The javac utility compiles Java source code contained in an ASCII file into a Java executable, composed of binary class files. The resulting binary class files can be used on any Java platform; transfer them in binary mode to the target system.
When the javac command compiles source files, it uses the current CLASSPATH setting to find classes that the source files are dependent on. The javac utility provides a limited dependency-checking capability to assist in building Java projects. If javac finds source files for dependent classes before it finds the class files, it compiles the source files to get the dependent classes. Similarly, if javac finds both the source files and the class files for the dependent classes, but the class files are older than the source files, the source files for the dependent classes are recompiled.
The syntax of the javac command to compile Java source code is as follows :
javac [options] ...
Depending on the version of the javac command that you use, different option parameters are available. Table 23.3 shows the more commonly used javac options.
Option |
Description |
---|---|
-classpath < path > |
Specifically set the CLASSPATH environment variable of the Java runtime environment created. The value of this parameter over-rides the current setting of CLASSPATH. |
-O |
Enable optimization. |
-g |
Enable debugging of the resulting class files. |
-verbose |
Use the -verbose option to log information about various compilation steps and when and from where each class is loaded. |
- deprecation |
Output detailed messages about deprecated APIs. Java marks an API as deprecated when a more appropriate API has replaced the original one. Try to avoid using deprecated APIs. |
-d < directory > |
Output the class files to a directory other than the directory in which the source files are located. Directories that match the packages defined in the Java classes are created as required. |
-encoding |
Specify the character encoding used by the Java source files. |
The javadoc utility provides a standard mechanism by which a Java developer can provide API documentation for the classes that they develop. Delivering documentation is a critical step in developing classes and APIs in a team, department, or corporate environment. Other developers need to be able to understand the services provided.
In a hectic development environment, it is very easy for documentation and APIs to become hopelessly out of date in relation to each other. The Java language addresses this problem by combining source code and documentation in a simple and standard way. It defines the syntax of the source-code comments, and the tags used in those comments, to define detailed descriptions of the code.
The javadoc utility parses Java source code similar to the javac compiler. Instead of generating binary code from the source statements, however, the javadoc utility focuses on the Java comments. It looks at packages, class declarations, and method declarations, and is very tolerant of Java source code that does not compile.
The javadoc utility uses the comments and tags to generate HTML documentation describing the APIs. If run against a group of Java source files, the HTML documentation automatically generates a table of contents and an index. The group of files and the classes and methods contained within are automatically cross-referenced in a way that enables even the simplest documentation to become fairly professional and useful. However, the javadoc utility supports a varied and robust set of tags (in addition to HTML tags and links), so that a developer can generate as detailed and feature-rich a set of documentation as desired.
You can generate useful javadoc documentation without using any of the special javadoc tags that are inserted into those comments. Simply use the javadoc comment immediately preceding each class, variable, and method that you declare. Regular multiline Java comment start with /* and end with */. A javadoc comment starts with /** and ends with */.
The javadoc utility will even generate documentation for Java source with no comments. The generated documentation will still have the class, interface, method names , and types. The documentation will also have an index and cross-references generated in a very useful fashion.
The javadoc utility is a bit more flexible in the type of parameters it accepts than other Java utilities. Javadoc accepts Java package names, source file names, individual Java class names, or text files that contain lists of option parameters or Java package names.
Here is the syntax of the javadoc command to generate documentation from Java source code:
javadoc [options] [packages] [sourcefiles] [classnames] [@files]
Table 23.4 describes some of the basic javadoc options.
Option |
Description |
---|---|
-help |
Show help for options and parameters. |
-sourcepath < path > |
Find the java source files associated with a list of classes or packages passed on the command line. |
-classpath < path > |
Set the CLASSPATH of the javadoc environment created. Individual CLASSPATH entries are separated by colons. The CLASSPATH is used to assist in the lookup of Java source files for those classes and packages specified on the command line. |
-d < directory > |
Output the HTML files to a specific directory. Many HTML files and subdirectories, matching packages defined in the Java classes, are created as required. |
-windowtitle " title " |
Set the title that will be used in the HTML browser when browsing the documentation. |
-public |
Show only the public classes and methods in the generated documentation. Use this option for generating external documentation |
-protected |
Show protected and public classes and methods in the generated documentation. This is the default. Use it for generating external documentation. |
-private |
Show all classes and methods regardless of the access specification. Use this to generate internal documentation. |
-stylesheetfile < file > |
Use this option to specify your own style sheet for customization of the javadoc style. |
-version |
Include @ version tags in the documentation. |
-author |
Include @ author tags in the documentation. |
Table 23.5 describes some of the more commonly used javadoc documentation tags.
Tag |
Description |
---|---|
@author < text > |
Use this tag to copy all subsequent text to the documentation. Commas separate contents from multiple author tags. |
@deprecated < text > |
Use this tag to indicate that the class or method is old and should no longer be used. The first sentence is copied to the summary section of the documentation. Subsequent sentences/lines can describe more details. |
@exception classname text |
The same as the @ throws tag. |
@param name text |
Add a parameter and its description to the "parameters" section of the documentation. The text description describes the parameter, and can be multiple lines. |
@return text |
Describe the return value of the method. The text description can be multiple lines. |
@see target |
Create a reference to another piece of documentation. Among other values, specify target as a package, class, interface, field or method, followed by optional text that you want to display for the link. The target doesn't need to be fully qualified if its referenced in the code, but you can fully qualify it as in the following: package.Class#method(Class,Class, ) |
@throws classname text |
The same as the @ exception tag. Generate a "throws" subheading to the documentation and a reference to the class name. The text should describe the error condition as appropriate, and it may be multiple lines. |
@version text |
Adds a "version" subheading to the generated documentation. |
Figure 23.1 illustrates the javadoc utility. The documentation is output to the "doc" directory, and is then archived into a single file using the jar utility. The archive can be moved to a workstation or distributed as appropriate.
cat com/mcpress/qshell/JavaDocExample.java package com.mcpress.qshell; /** * This class represents a simple example for * using javadoc comments. The first line of * every javadoc comment is a summary of the class * or method, while the subsequent lines of the * comments are used for the detailed description * that the javadoc utility generates. * * @author Joe Smith * @version 1.0 */ public class JavaDocExample { /** * The internally maintained application name */ private String name = null; /** * The name of the database table used to lookup * the customer */ private String customerDatabase = null; /** * The getApplicationName() method returns the name * of the application. The name of the application * is generated randomly based on the time of * day. * * @return String representing the application name * @see com.mcpress.qshell.JavaDocExample#getName * @deprecated */ public String getApplicationName() { return null; } /** * The getName() method returns the true name of * the application. The name of the application is * the full name of the class containing the main * method. * * @return String representing the application name * @since 1.1 */ public String getName() { return null; } /** * The lookupCustomer() method looks up a customer * based on customer ID. The customer ID is the ID * that we print on all invoices and use in database * tables to uniquely identify a customer. * * @param number Unique customer ID * * @return The customer name or null if the * customer wasn't found */ public String lookupCustomer(int number) { } } mkdir doc ls com/mcpress/qshell/*.java com/mcpress/qshell/Args.java com/mcpress/qshell/CustomerDBLookup.java com/mcpress/qshell/CustomerList.java com/mcpress/qshell/CustomerLookup.java com/mcpress/qshell/HelloJava.java com/mcpress/qshell/JavaDocExample.java c om/mcpress/qshell/LineCounter.java com/mcpress/qshell/PtfStatus.java com/mcpress/qshell/Reverse.java javadoc -d doc -private -windowtitle "Qshell javadoc example" com/mcpress/qshell/*.java Loading source file com/mcpress/qshell/Args.java... Loading source file com/mcpress/qshell/CustomerDBLookup.java... Loading source file com/mcpress/qshell/CustomerList.java... Loading source file com/mcpress/qshell/CustomerLookup.java... Loading source file com/mcpress/qshell/HelloJava.java... Loading source file com/mcpress/qshell/JavaDocExample.java... Loading source file com/mcpress/qshell/LineCounter.java... Loading source file com/mcpress/qshell/PtfStatus.java... Loading source file com/mcpress/qshell/Reverse.java... Constructing Javadoc information... Building tree for all the packages and classes... Building index for all the packages and classes... Generating doc/overview-tree.html... Generating doc/index-all.html Generating doc/deprecated-list.html... Building index for all classes... Generating doc/allclasses-frame.html... Generating doc/index.html... Generating doc/packages.html... Generating doc/com/mcpress/qshell/Args.html... Generating doc/com/mcpress/qshell/CustomerDBLookup.html... Generating doc/com/mcpress/qshell/CustomerList.html... Generating doc/com/mcpress/qshell/CustomerLookup.html... Generating doc/com/mcpress/qshell/HelloJava.html... Generating doc/com/mcpress/qshell/JavaDocExample.html... Generating doc/com/mcpress/qshell/LineCounter.html... Generating doc/com/mcpress/qshell/PtfStatus.html... Generating doc/com/mcpress/qshell/Reverse.html... Generating doc/serialized-form.html... Generating doc/package-list... Generating doc/help-doc.html... Generating doc/stylesheet.css... ls doc allclasses-frame.html index-all.html packages.html com index.html serialized-form.html deprecated-list.html overview-tree.html stylesheet.css help-doc.html package-list jar cf doc.jar doc
Figure 23.1: This example demonstrates using the javadoc command on a group of classes.
The doc.jar file from Figure 23.1 is transferred to a client workstation and extracted. Figure 23.2 shows what the documentation looks like when a browser displays the doc/index.html file. Because the set of classes used in Figure 23.1 only defines one package, there is a table of contents for the individual classes, but not for the packages.
Figure 23.2: Javadoc documentation may be viewed with a Web browser.
The jar utility provides support for creating and extracting Java archives. A Java archive is a zip-format file that typically contains java class files, although a jar file may contain any file type. The Java archive also contains additional information ( meta-information ), added by the jar utility to describe the contents of the archive file and attributes of the Java classes or applications in the file. Use a jar file as part of the CLASSPATH to allow the java runtime access to the classes or other resources in the jar file.
The syntax of the jar utility, shown below, is unique compared to other Qshell utilities:
jar [options] <-C directory>
Note that the option parameters must be grouped together, and they do not require leading dashes (except for the - C parameter). The jar utility accepts option parameters ( m and f ) that require file names, but those file names are not specified immediately following the parameter. The utility recursively processes any directories named on the command line.
Table 23.6 shows values for the action option. At least one of the values in Table 23.6 must be specified.
Key Sequence |
Description |
---|---|
c |
Create the jar file specified as a new archive file. |
t |
List the contents of the jar file specified. |
x |
Extract the contents of the jar file specified. |
u |
Update the contents of an existing jar file. |
Table 23.7 shows other options for the jar utility. If the f and m options are both used, specify the jar-file and manifest-file parameters in the same order as the options parameters.
Key Sequence |
Description |
---|---|
v |
Process the files verbosely, generating additional output for each file processed . |
f |
Specify the java archive file is specified. Always use the f parameter. If the m option is also specified, use the associated jar-file and manifest-file parameters in the same order as the f and m options. |
m |
Specify a manifest file. If the f option is also specified, use the associated jar-file and manifest-file parameters in the same order as the f and m options. |
0 (zero) |
Store the files in the Java archive without using zip compression. |
M |
Don't automatically create a manifest file at META-INF/MANIFEST.MF in the archive file. |
i |
Generate index information for the jar files specified. |
-C dir |
Change to the directory name specified before archiving the target file or directory names. |
In Figure 23.3, the jar utility is used to zip the java source and class files. The output is produced due to inclusion of the v option.
pwd /home/jsmith/src jar cvf sourceAndBinaries.jar com added manifest adding: com/(in = 0) (out= 0)(stored 0%) adding: com/mcpress/(in = 0) (out= 0)(stored 0%) adding: com/mcpress/qshell/(in = 0) (out= 0)(stored 0%) adding: com/mcpress/qshell/CustomerList.class(in = 2091) (out= 1173)(deflated 43%) adding: com/mcpress/qshell/CustomerList.java(in = 1710) (out= 587)(deflated 65%) adding: com/mcpress/qshell/CustomerLookup.class(in = 2791) (out= 1557)(deflated 44%) adding: com/mcpress/qshell/CustomerLookup.java(in = 3967) (out= 1214)(deflated 69%) adding: com/mcpress/qshell/HelloJava.class(in = 599) (out= 367) (deflated 38%) adding: com/mcpress/qshell/HelloJava.java(in = 197) (out= 144) (deflated 26%) adding: com/mcpress/qshell/CustomerDBLookup.class(in = 2879) (out= 1671)(deflated 41%) adding: com/mcpress/qshell/CustomerDBLookup.java(in = 4097) (out= 1350)(deflated 67%) adding: com/mcpress/qshell/PtfStatus.class(in = 2401) (out= 1333)(deflated 44%) adding: com/mcpress/qshell/PtfStatus.java(in = 3434) (out= 1062) (deflated 69%)
Figure 23.3: Use the jar utility from your base source-file directory to zip the java source and class files.
Jar files are compatible with the WinZip and PKZip utilities, so you can also use jar as a convenient command-line zip/unzip utility to manipulate zip archives, if no other archive tool is present. Using the M option, as in Figure 23.4, ensures that no manifest files are added to the archives. Adding the v option to the t option generates a listing that contains information about the files' sizes and dates.
jar cfM classes.jar com/mcpress/qshell/*.class jar tvf classes.jar 2879 Thu Jun 05 02:51:20 UTC 2003 com/mcpress/qshell/CustomerDBLookup.class 2091 Thu Jun 05 02:47:58 UTC 2003 com/mcpress/qshell/CustomerList.class 2791 Thu Jun 05 02:47:58 UTC 2003 com/mcpress/qshell/CustomerLookup.class 599 Thu Jun 05 02:47:58 UTC 2003 com/mcpress/qshell/HelloJava.class 2401 Fri Jun 06 05:36:52 UTC 2003 com/mcpress/qshell/PtfStatus.class
Figure 23.4: Use the jar utility to create and list the contents of an archive. The M option prevents a META-INF/MANIFEST.MF file from being created in the archive file.
Starting with a typical "Hello World" example and working up to more complex techniques, the code samples in this section show various ways that your Java application can interact with Qshell. The examples for phone-number lookup, PTF status, and database access are very similar to those coded in perl in chapter 22, and in C in chapter 24. These implementations of similar programs in three different languages provide you with a good basis for comparing perl, Java, and C.
The example in Figure 23.5 shows Java source code and an appropriate directory structure for Java application development. It demonstrates the javac utility and the java utility. The CLASSPATH environment variable is set to the base location of classes. The package name further qualifies the classes stored in that location by modifying both the full class name and the java/class file location. The Java application sets a Qshell exit status.
pwd /home/jsmith/src find /home/jsmith/src /home/jsmith/src /home/jsmith/src/com /home/jsmith/src/com/mcpress /home/jsmith/src/com/mcpress/qshell /home/jsmith/src/com/mcpress/qshell/HelloJava.java cat com/mcpress/qshell/HelloJava.java package com.mcpress.qshell; import java.lang.*; class HelloJava { public static void main(String args[]) { System.out.println("Hello Java"); System.exit(5); } } export CLASSPATH=/home/jsmith/src javac com/mcpress/qshell/HelloJava.java find /home/jsmith/src /home/jsmith/src /home/jsmith/src/com /home/jsmith/src/com/mcpress /home/jsmith/src/com/mcpress/qshell /home/jsmith/src/com/mcpress/qshell/HelloJava.class /home/jsmith/src/com/mcpress/qshell/HelloJava.java java com.mcpress.qshell.HelloJava Hello Java echo $? 5 unset CLASSPATH java -classpath /home/jsmith/src com.mcpress.qshell.HelloJava Hello Java
Figure 23.5: This example shows Java source code and an appropriate directory structure for Java application development.
Figure 23.6 demonstrates a customer list in Java. This example is used as a building block for the more complex examples that follow.
cat com/mcpress/qshell/CustomerList.java package com.mcpress.qshell; import java.util.Hashtable; public class CustomerList { public final static String NONE = "none"; Hashtable table; public CustomerList() { table = new Hashtable(); table.put("Chuck", "444-2345"); table.put("Bubba", "444-1111"); table.put("Billy Bob", "444-4340"); table.put("Amos", "333-1119"); table.put("Otis", "444-8000"); table.put("Claude", "333-4340"); table.put("Roscoe", "444-2234"); table.put("Arlis", "444-1314"); table.put("Junior", "BR-549"); table.put("Bill", "333-4444"); table.put("Ernest T.", NONE); } public String lookup (String name) { return (String)table.get(name); } public static void main(String args[]) { if (args.length == 0) { System.err.println("Usage: java CustomerList [name-to-lookup]"); System.exit(1); } CustomerList list = new CustomerList(); String item; for (int i=0; i item = list.lookup(args[i]); if (item == null) { System.err.println("Didn't find customer:" + args[i]); } else { if (item == NONE) { System.err.println("No listing for" + args[i]); } else { System.out.println("Found" + args[i] + "at" + item); } } } // end of for (i=0 < args.length) System.exit(0); } // end of main } export CLASSPATH=/home/jsmith/src javac com/mcpress/qshell/CustomerList.java java -cp /home/jsmith/src com.mcpress.qshell.CustomerList Chuck Bubba Found Chuck at 444-2345 Found Bubba at 444-1111
Figure 23.6: This example demonstrates a simple command-line lookup utility. This application has the data written directly into the Java application. Expanding this example to read in the data would produce a more flexible application.
Using the goodoleboys.txt data file (from Figure 21.1), Figure 23.7 demonstrates a functioning, interactive phone-number lookup application. This application uses two Hashtable objects to provide lookup services based on the data file. Compare and contrast this code with the similar perl example in chapter 22 (Figure 22.7) and the C example in chapter 24 (Figure 24.5).
pwd /home/jsmith/src cat com/mcpress/qshell/CustomerLookup.java package com.mcpress.qshell; import java.util.Hashtable; import java.io.*; public class CustomerLookup { public final static String NONE = "none"; // Two Hashtables mapping one object to another Hashtable numberByOleBoy = new Hashtable(); Hashtable oleBoyByWife = new Hashtable(); public CustomerLookup(String filename) throws IOException { // Use a LineNumberReader on top of a FileInputStream object. LineNumberReader in = new LineNumberReader( new InputStreamReader( new FileInputStream(filename) )); // Read each line and split into its components. String line = in.readLine(); while (line != null) { String oleBoy = line.substring(0,10); oleBoy = oleBoy.trim(); String oleBoysWife = line.substring(37, 47); oleBoysWife = oleBoysWife.trim(); String phoneNumber = line.substring(19, 27); phoneNumber = phoneNumber.trim(); if (phoneNumber.equals("none")) { phoneNumber = NONE; } numberByOleBoy.put(oleBoy, phoneNumber); oleBoyByWife.put(oleBoysWife, oleBoy); line = in.readLine(); } } /** * Lookup a phone number based on the name of the customer or * his wife. Return CustomerLookup.NONE if the customer has no * phone, Return the phone number string if found. Return null * if not found. */ public String lookup(String name) { String phoneNumber = (String)numberByOleBoy.get(name); if (phoneNumber == null) { String oleboy = (String)oleBoyByWife.get(name); if (oleboy != null) { phoneNumber = (String)numberByOleBoy.get(oleboy); } } return phoneNumber; } public static void main(String args[]) { if (args.length != 1) { System.err.println("Usage: java CustomerLookup data-file"); System.exit(1); } try { CustomerLookup list = new CustomerLookup(args[0]); String item; String name; // Get a 'nicer' interface over the System.in object // that represents standard input. We'll use LineNumberReader // just for fun. LineNumberReader in = new LineNumberReader(new InputStreamReader(System.in)); while (true) { System.out.println("Enter 'exit' or a name to lookup:"); // Read the next name from standard input, any input // class (API) that could be constructed from System.in // could be used here. name = in.readLine(); if (name == null) { // All done. break; } if (name.equals("exit") name.equals("quit")) { break; } // Lookup the name item = list.lookup(name); if (item == null) { System.err.println("Didn't find customer:" + name); } else { if (item == NONE) { System.err.println("No listing for" + name); } else { System.out.println("Found" + name + "at" + item); } } } // Exit successfully. System.exit(0); } catch (Exception e) { System.err.println("Error occurred"); e.printStackTrace(); // Exit in error condition. System.exit(2); } } // end of main } export CLASSPATH=/home/jsmith/src javac com/mcpress/qshell/CustomerLookup.java java com.mcpress.qshell.CustomerLookup /home/jsmith/src/goodoleboys.txt Enter 'exit' or a name to lookup: Bill Found Bill at 333-4444 Enter 'exit' or a name to lookup: Daisy Found Daisy at 333-4444 Enter 'exit' or a name to lookup: Claude Found Claude at 333-4340 Enter 'exit' or a name to lookup: Etheline Found Etheline at 333-4340 Enter 'exit' or a name to lookup: Fred Didn't find customer: Fred Enter 'exit' or a name to lookup: exit
Figure 23.7: This fully functioning Java example reads in data from a file and provides a lookup of a customer's phone number by the customer name or the name of his wife.
Several things in this example are worth noticing:
The programmer always needs to create a Java class, but this example is also different because we took a more object-oriented approach to the problem. We provided a CustomerLookup object that performed the work in a rather abstract way. The main routine simply creates a CustomerLookup object based on a file, and then uses the lookup method without concern for its internals.
That additional work can be viewed as pure overhead, or as good software design. In the case of this small example, it is probably more overhead. You can probably just as easily write a small program in perl or C as in Java. Because of its object-oriented nature, however, you might find it easier to write Java software that isolates its internal structures from other parts of the application.
Like perl, Java provides the ability to invoke a Qshell command. Figure 23.8 uses the Qshell system utility to interact directly with a CL command. Recall from chapter 22 that invoking the utility like this is a shortcut, which may use a temporary file to hold the output from a Qshell or CL command or utility.
pwd /home/jsmith/src cat com/mcpress/qshell/PtfStatus.java package com.mcpress.qshell; import java.util.*; import java.io.*; public class PtfStatus { public final static boolean debug = false; Hashtable ptfs; public PtfStatus() throws Exception { ptfs = new Hashtable(); String commandArray[] = new String[]{ "/usr/bin/system", "DSPPTF OUTPUT(*PRINT)" }; Runtime run = Runtime.getRuntime(); Process proc = run.exec(commandArray); // Since we're running a Qshell command, the process // will output EBCDIC data. We'll use an encoding of // Cp037 to read US Englich EBCDIC (CCSID 37) LineNumberReader in = new LineNumberReader (new InputStreamReader(proc.getInputStream(), "Cp037")); // Read each line. String line = in.readLine(); while (line != null) { if (debug) { System.out.println("<" + line + ">"); } // PTF list in the output is a 7 character name, // 2 alpha, 5 digits or // 3 alpha, 4 digits // prefixed by one space on each line. boolean isAPtf = true; isAPtf = isAPtf && Character.isSpace(line.charAt(1)); isAPtf = isAPtf && Character.isLetter(line.charAt(2)); isAPtf = isAPtf && Character.isLetter(line.charAt(3)); isAPtf = isAPtf && Character.isLetterOrDigit(line.charAt(4)); isAPtf = isAPtf && Character.isDigit(line.charAt(5)); isAPtf = isAPtf && Character.isDigit(line.charAt(6)); isAPtf = isAPtf && Character.isDigit(line.charAt(7)); isAPtf = isAPtf && Character.isDigit(line.charAt(8)); isAPtf = isAPtf && Character.isSpace(line.charAt(9)); if (isAPtf) { String ptf = line.substring(2, 9); String status = line.substring(14, 33); if (debug) { System.out.println("OUT: <" + ptf + ">, <" + status + ">"); } ptfs.put(ptf, status); } line = in.readLine(); } } public String getStatus(String name) { String status = (String)ptfs.get(name); if (status == null) { status = "Not Loaded"; } return status; } public static void main(String args[]) { if (args.length == 0) { System.err.println("Usage: java PtfStatus ..."); System.exit(1); } try { PtfStatus ptfs = new PtfStatus(); String status; System.out.println("------- --------------------"); System.out.println(" PTF Status "); System.out.println("------- --------------------"); for (int i=0; i status = ptfs.getStatus(args[i]); System.out.print(args[i]); System.out.print(" "); System.out.println(status); } // end of for (i=0 < args.length) } catch (Exception e) { e.printStackTrace(); System.exit(2); } System.exit(0); } // end of main } java com.mcpress.qshell.PtfStatus SI06971 SI06972 SI06973 SI06974 SI06975 SI06976 SI06977 ------- -------------------- PTF Status ------- -------------------- SI06971 Temporarily applied SI06972 Superseded SI06973 Not Loaded SI06974 Not Loaded SI06975 Temporarily applied SI06976 Temporarily applied SI06977 Superseded
Figure 23.8: This Java example demonstrates using the Runtime.exec() API to run a Qshell command and process the output.
Figure 23.8 uses the Runtime.exec() API to process the output from a Qshell command. The Qshell system utility is used to execute a CL command. The output is used to build a Hashtable containing the PTF (Program Temporary Fix) names and their statuses. The Hashtable serves as the lookup mechanism for the status of each randomly selected PTF. Compare and contrast this Java example with the similar perl example in Figure 22.9 and the C example in Figure 24.7.
Notice, in this example, that running a Qshell utility from within Java is almost as simple as in the perl program. You have to do a bit more processing related to the code page of the data sent back from the program, but the difficulty is minimal. Also, notice that the regular expression features of perl are missing (in Java 1.3). Although the method used here might perform better for this example, regular expressions would be a useful alternative to this model for finding a PTF in a line of text. Regular expression support is present in Java 1.4.
The Java language provides a standard way to access a database using JDBC ( Java Database Connectivity ) APIs. Figure 23.9 uses these APIs to demonstrate a more realistic phone-number lookup application than in Figure 23.7. The names and phone numbers reside in the relational database on the iSeries; Java is used to access this database. Compare and contrast this Java example with the similar perl example in Figure 22.10 and the C example in Figure 24.8.
pwd /home/jsmith/src cat com/mcpress/qshell/CustomerDBLookup.java package com.mcpress.qshell; import java.util.Hashtable; import java.io.*; import java.sql.*; // Get the JDBC classes public class CustomerDBLookup { public final static String NONE = "none"; Connection conn; PreparedStatement ps; static { // Load the JDBC driver that we're going to use. // JDBC drivers register themselves when loaded. try { Class.forName("com.ibm.as400.access.AS400JDBCDriver"); } catch (Exception e) { System.err.println("Didn't load the toolbox driver"); } } public CustomerDBLookup() throws SQLException { conn = DriverManager.getConnection("jdbc:as400:localhost"); ps = conn.prepareStatement ("SELECT PHONENUMBER FROM JSMITHQ.CUSTOMERS" + "WHERE NAME = ? OR WIFE = ?"); } public String lookup(String name) throws SQLException { // Everything is already created. Just set the parameters // and execute the statement. ps.setString(1, name); ps.setString(2, name); ResultSet rs = ps.executeQuery(); String phoneNumber = null; if (rs.next()) { // Get the first column from the first row of the result set. phoneNumber = rs.getString(1); if (phoneNumber == null) { // Found the customer, but no phone number phoneNumber = NONE; } } rs.close(); return phoneNumber; } public void close() { if (conn != null) { try { conn.close(); } catch (Exception e) { e.printStackTrace(); } } conn = null; ps = null; } public static void main(String args[]) { if (args.length != 0) { System.err.println("Usage: java CustomerDBLookup"); System.exit(1); } CustomerDBLookup list = null; try { String item; String name; list = new CustomerDBLookup(); // Get a 'nicer' interface over the System.in object // that represents standard input. We'll use LineNumberReader // just for fun. LineNumberReader in = new LineNumberReader(new InputStreamReader(System.in)); while (true) { System.out.println("Enter 'exit' or a name to lookup:"); Read the next name from standard input, any input // class (API) that could be constructed from System.in // could be used here. name = in.readLine(); if (name == null) { // All done. break; } if (name.equals("exit") name.equals("quit")) { break; } // Lookup the name item = list.lookup(name); if (item == null) { System.err.println("Didn't find customer:" + name); } else { if (item == NONE) { System.err.println("No listing for" + name); } else { System.out.println("Found" + name + "at" + item); } } } // Exit successfully. System.exit(0); } catch (Exception e) { System.err.println("Error occurred"); e.printStackTrace(); // Exit in error condition. System.exit(2); } finally { // Be sure to clean up any DB resources that were allocated. if (list != null) { list.close(); } } } // end of main } export CLASSPATH=/home/jsmith/src java com.mcpress.qshell.CustomerDBLookup Didn't load the toolbox driver Error occurred java.sql.SQLException: No suitable driver java/lang/Throwable.(Ljava/lang/String;)V+4 (Throwable.java:90) java/sql/SQLException.(Ljava/lang/String;Ljava/lang/String;)V+1 (SQLException.java:60) java/sql/DriverManager.getConnection(Ljava/lang/String;Ljava/util/ Properties;Ljava/lang/ClassLoader;)Ljava/sql/Connection;+ 243 (DriverManager. java:537) java/sql/DriverManager.getConnection(Ljava/lang/String;)Ljava/sql/ Connection;+12 (DriverManager.java:199) com/mcpress/qshell/CustomerDBLookup.()V+0 (CustomerDBLookup.java:22) com/mcpress/qshell/CustomerDBLookup.main([Ljava/lang/String;)V+0 (CustomerDBLookup.java:58) export CLASSPATH=/QIBM/ProdData/HTTP/Public/jt400/lib/jt400.jar: $CLASSPATH java com.mcpress.qshell.CustomerDBLookup Enter 'exit' or a name to lookup: Bill Found Bill at 333-4444 Enter 'exit' or a name to lookup: Daisy Found Daisy at 333-4444 Enter 'exit' or a name to lookup: Claude Found Claude at 333-4340 Enter 'exit' or a name to lookup: Etheline Found Etheline at 333-4340 Enter 'exit' or a name to lookup: Fred Didn't find customer: Fred Enter 'exit' or a name to lookup: exit
Figure 23.9: Use the database to perform the phone number lookup previously demonstrated using a Hashtable.
Except for the need to load the JDBC driver before accessing the database, this example looks similar to the perl program. The database-access APIs are object-based in both perl and Java, so they are very similar.
The Java program has some requirements with regard to exception-handling that aren't present in the other languages. Java exceptions eliminate some of the need for error-checking in the mainline program path . At times, handling exceptions for possible or expected error conditions can be slightly more complex in Java than error-checking in other languages.
The commands shown in Figure 23.9 also demonstrate the type of error that can occur if the CLASSPATH variable is not set up correctly:
java.sql.SQLException: No suitable driver
Fixing the CLASSPATH variable enables the example program to run successfully.
The Qshell environment is an integral part of Java application development. Java applications have built-in support for interacting with Qshell, and many Java commands are only accessible from within Qshell. The Java jar utility can serve as a general-purpose archive and file-compression utility.
Preface