Logo: TUG TORONTO USERS GROUP for Midrange Systems
TUG e -server magazine

March 2003: Volume 18, Number 4


IBM Toolbox for Java
IBM Toolbox for JAVA (What's New in V5R2?)


New Functionality gives users more power and flexibility

* This article appeared in the TUG eServer magazine March 2003 issue. (V18N4)
* Reprinted with permission from MC Mag Online, published by MC Press, LLC, CA: http://www.mcpressonline.com

Redline

Robb Wiedrich By Robb Wiedrich

s I mentioned in my article "What's in the IBM Toolbox for Java V5R1?", IBM Toolbox for Java is a set of Java classes that allow you to access iSeries data through a Java program. With these classes, you can write client/server applications, applets, and servlets that work with data on your iSeries. You can also run Java applications that use the IBM Toolbox for Java on the iSeries Java Virtual Machine (JVM).

In V5R2, the IBM Toolbox for Java has added functionality that gives users more power and flexibility. The new functions being delivered with V5R2 are designated as Modification 5. The major new IBM Toolbox for Java components for V5R2 include the iSeries System Debugger (a debugging environment for ILE, Java, C, and C++ programs that run on an iSeries server) and the following micro classes (com.ibm.as400.micro package):

The Toolbox also features these new classes:

Along with new functions, the IBM Toolbox for Java now provides support for the following:

If you intend to run a Java program that uses IBM Toolbox for Java classes on the iSeries JVM, you must run the IBM Toolbox for Java at a version and release level that's compatible with the version and release of OS/400 that is running on your system. OS/400 ships with the parts of IBM Toolbox for Java that are needed to improve performance when your application is running on the iSeries JVM. Use the chart in Figure 1 to ensure compatibility.

Toolbox Modification
Ships with OS/400
LPP
Installs on
OS/400
Connects Back to OS/400
Mod 0*
V4R2
5763-JC1 V3R2M0
V3R2 and above
V3R2 and above
Mod 1*
V4R3
5763-JC1 V3R2M1
V3R2 and above
V3R2 and above
Mod 2*
V4R4
5769-JC1 V4R2M0
V4R2 and above
V4R2 and above
Mod 3
V4R5
5769-JC1 V4R5M0
V4R3 and above
V4R2 and above
Mod 4
V5R1
5722-JC1 V5R1M0
V4R4 and above
V4R3 and above
Mod 5
V5R2
5722-JC1 V5R2M0
V4R5 and above
V4R3 and above
Figure 1. Be sure the versions and release levels of the IBM Toolbox for Java and OS/400 are compatible.

Micro Classes

The micro classes that are provided as part of IBM Toolbox for Java 2 Micro Edition (ToolboxME) allow you to access iSeries data from a wireless device like a cell phone or PDA. ToolboxME focuses its attention on iSeries access to commands, programs, data queues, and databases. The ToolboxME classes provide only a subset of the function that is available in the IBM Toolbox for Java because the wireless devices are very small and resource constrained.

(From the TUG Web site, you can download a sample ToolboxME application using the AS400, CommandCall, ProgramCall, and DataQueue classes.) Figure 2 shows the screen shots of what the application looks like in a typical wireless device emulator.

Figure 2. Screen shots of iSeries applications rendered on wireless device emulators.

You can also download a sample ToolboxME application using the JdbcMe classes. Figure 3 shows the resulting screen shots of what the application looks like in different wireless device emulators.

Figure 3. The way the application appears depends upon the wireless device emulator.

iSeries System Debugger

The iSeries System Debugger consists of the following components:

Figure 4. The graphical Debug Manager manages your debugging operations.

Debug Manager

The Debug Manager registers the client with the Debug Hub, which enables the graphical debugging mode for the selected systems. Once the client is registered, a user can issue the Start Debug (STRDBG) CL command from an emulation session to start the System Debugger.

Use the Debug Manager to manage your debugging operations and connections:



System Debugger

The System Debugger is used to debug programs that run on the iSeries server. You can debug programs that are running in existing jobs on the system or use the System Debugger to launch and then debug programs in a system batch job. You can set up the System Debugger to start automatically, start manually from a workstation command prompt, or start by using the Debug Manager interface.

Use the System Debugger to perform debugging activities, including the following:

Figure 5. The System Debugger performs your debugging activities.


Debug Hub

The Debug Hub serves as a registry for clients that want to use the System Debugger. It also handles incoming requests to start debug servers.

Use the Debug Manager interface to register your client with the Debug Hub. Registering a client stores both the user information and the TCP/IP address of the client in the registry. Using the STRDBG command from an emulation session contacts the Debug Hub to see if the user executing the command is registered with the Debug Manager. It also checks to see if the command being executed is from the same TCP/IP address as the Debug Manager. When these qualifications are met, the graphical iSeries System Debug application is started instead of the traditional debug environment.

The Debug Hub also serves as a single point of contact for all iSeries System Debugging applications. When an iSeries System Debugger performs a start debug operation, the Debug Hub submits a Debug Server job on the user's behalf and passes it the associated TCP/IP connection.

Debug Server

The Debug Server is a TCP/IP server that is started by the Debug Hub when the System Debugger issues a request to start debugging. The server job then services the job that is being debugged and issues the appropriate debugger APIs and commands.

ClusteredHashTable Classes

The ClusteredHashTable classes enable your Java programs to use highly available clustered hash tables to share and replicate data to non-persistent storage among the nodes in a cluster. They provide methods that enable you to perform the following actions:

The example in Figure 6 operates on clustered hash table server named CHTSVR01.

CommandPrompter Classes

The CommandPrompter class prompts for the parameter on a given command. The CommandPrompter offers functionality that is similar to the iSeries CL command prompt (pressing F4).

RecordFormatDocument Classes

The Record Format Markup Language (RFML) is an XML extension for specifying record formats. The IBM Toolbox for Java RFML component enables your Java applications to use RFML documents to specify and manipulate fields within certain kinds of records.

RFML documents, called RFML source files, represent a useful subset of the data description specifications (DDS) data types defined for physical and logical files on iSeries systems. You can use RFML documents to manage the information in file records, data queue entries, user spaces, and arbitrary data buffers.

JTOpen – Open-Source Toolbox

The IBM Toolbox for Java has joined the ranks of the open-source community with JTOpen, the open-source Toolbox for Java and IBM’s first iSeries product to go open source. JTOpen currently uses the Mod 5 (V5R2) Toolbox code base, which runs on a V4R5 or higher iSeries and any client or server with a JVM. The IBM Toolbox for Java will continue to ship with each release of the iSeries, but with the JTOpen version, users can get up-to-the minute improvements.

There are a number of reasons why open sourcing the Toolbox is beneficial:

JTOpen is available from the Concurrent Versioning System (CVS) repository, a system that allows anybody to work simultaneously on groups of files. A list of guidelines and contacts is posted for those who want to participate.

The JTOpen Core Team manages the project and currently consists of three IBM employees and two non-IBM members (including MC Press author Joe Pluta) who ensure that developer contributions are of good quality and will be useful to the community. New functionality that is committed to JTOpen will not necessarily be added into the Toolbox LPP. However, new function that exists in future releases of the Toolbox LPP may also get submitted to the JTOpen repository, at IBM’s discretion. The result is that JTOpen will become a superset of the Toolbox LPP.

JTOpen 3.1, which is the latest release, has the following enhancements:

ClusteredHashTableEntry myEntry = null;

String myData = new String("This is my data.");

System.out.println("Data to be stored: " + myData);

AS400 system = new AS400();

ClusteredHashTable cht = new ClusteredHashTable(system,"CHTSVR01");

// Open a connection.

cht.open();

// Get a key to the hash table.

byte[] key = null;

key = cht.generateKey();

// Prepare some data that you want to store into the hash table.

// ENTRY_AUTHORITY_ANY_USER means that any user can access the

// entry in the clustered hash table.

// DUPLICATE_KEY_FAIL means that if the specified key already exists,

// the ClusteredHashTable.put() request will not succeed.

int timeToLive = 500;

myEntry = new ClusteredHashTableEntry(key,myData.getBytes(),timeToLive,

ClusteredHashTableEntry.ENTRY_AUTHORITY_ANY_USER,

ClusteredHashTableEntry.DUPLICATE_KEY_FAIL);

// Store (or put) the entry into the hash table.

cht.put(myEntry);

// Get an entry from the hash table.

ClusteredHashTableEntry output = cht.get(key);

// Close the connection.

cht.close();

Figure 6. Sample code referencing clustered hash table.

The IBM Toolbox for Java in V5R2 has given developers unprecedented flexibility for building iSeries Java applications, applets, or servlets. And now, with the advent of JTOpen, the Toolbox looks to draw on the knowledge of the Toolbox user community. Visit the JTOpen site and join other developers in making the Toolbox the most powerful client/server Java package available. T < G


Robb Wiedrich is the Toolbox team leader at IBM Rochester. He has spent the last five years working on the IBM Toolbox for Java development team. Robb can be reached at wiedrich@us.ibm.com.



MORE CODING EXAMPLES:

Sample ToolboxME Application Using the AS400, CommandCall, ProgramCall, and DataQueue classes

/////////////////////////////////////////////////////////////////////////

//

// This source is an example of using the IBM Toolbox for Java 2 Micro Edition

// package classes, which allow you to easily build wireless applications.

// IBM grants you a nonexclusive license to use this as an example

// from which you can generate similar function tailored to

// your own specific needs.

//

// This sample code is provided by IBM for illustrative purposes

// only. These examples have not been thoroughly tested under all

// conditions. IBM, therefore, cannot guarantee or imply

// reliability, serviceability, or function of these programs.

//

// All programs contained herein are provided to you "AS IS"

// without any warranties of any kind. The implied warranties of

// merchantability and fitness for a particular purpose are

// expressly disclaimed.

//

// IBM Toolbox for Java 2 Micro Edition

// (C) Copyright IBM Corp. 2002

// All rights reserved.

// US Government Users Restricted Rights -

// Use, duplication, or disclosure restricted

// by GSA ADP Schedule Contract with IBM Corp.

//

/////////////////////////////////////////////////////////////////////////

import java.io.*;

import java.sql.*;

import java.util.Hashtable;

// These classes are part of the MIDP (Mobile Information Device Profile)

import javax.microedition.midlet.*;

import javax.microedition.lcdui.*;

import javax.microedition.rms.*;

// Toolbox Micro Edition classes.

import com.ibm.as400.micro.*;


/**

* A Toolbox MIDP sample application.

* <p>

* This application requires the qsyrusri.pcml file to be present in the CLASSPATH.

**/

public class ToolboxMidpDemo extends MIDlet implements CommandListener

{

private Display display_;

// A ToolboxME system object.

private AS400 system_;

private List main_ = new List("Toolbox MIDP Demo", Choice.IMPLICIT);

// Create a form for each component.

private Form signonForm_;

private Form cmdcallForm_;

private Form pgmcallForm_;

private Form dataqueueForm_;

private Form aboutForm_;

// Visable Text for each component.

static final String SIGN_ON = "SignOn";

static final String COMMAND_CALL = "CommandCall";

static final String PROGRAM_CALL = "ProgramCall";

static final String DATA_QUEUE = "DataQueue";

static final String ABOUT = "About";

static final String NOT_SIGNED_ON = "Not signed on.";

static final String DQ_READ = "Read";

static final String DQ_WRITE = "Write";

// A ticker to display the signon status.

private Ticker ticker_ = new Ticker(NOT_SIGNED_ON);

// Commands that can be performed.

private static final Command actionExit_ = new Command("Exit", Command.SCREEN, 0);

private static final Command actionBack_ = new Command("Back", Command.SCREEN, 0);

private static final Command actionGo_ = new Command("Go", Command.SCREEN, 1);

private static final Command actionClear_ = new Command("Clear", Command.SCREEN, 1);

private static final Command actionRun_ = new Command("Run", Command.SCREEN, 1);

private static final Command actionSignon_ = new Command(SIGN_ON, Command.SCREEN, 1);

private static final Command actionSignoff_= new Command("SignOff", Command.SCREEN, 1);

private Displayable onErrorGoBackTo_; // the form to return to when done displaying the error form

// TextFields for the SignOn form.

private TextField signonSystemText_ = new TextField("System", "mySystem", 20, TextField.ANY);

private TextField signonUidText_ = new TextField("UserId", "myUID", 10, TextField.ANY);

private TextField signonPwdText_ = new TextField("Password", "myPassword", 10, TextField.PASSWORD);

private TextField signonServerText_ = new TextField("MEServer", "myMEServer", 10, TextField.ANY);

private StringItem signonStatusText_ = new StringItem("Status", NOT_SIGNED_ON);

// TextFields for the CommandCall form.

private TextField cmdText_ = new TextField("Command", "CRTLIB FRED", 256, TextField.ANY);

private StringItem cmdMsgText_ = new StringItem("Messages", null);

private StringItem cmdStatusText_ = new StringItem("Status", null);

// TextFields for the ProgramCall form.

private StringItem pgmMsgDescription_ = new StringItem("Messages", null);

private StringItem pgmMsgText_ = new StringItem("Messages", null);

// TextFields for the DataQueue form.

private TextField dqInputText_ = new TextField("Data to write", "Hi there", 30, TextField.ANY);

private StringItem dqOutputText_ = new StringItem("DQ contents", null);

private ChoiceGroup dqReadOrWrite_ = new ChoiceGroup("Action", Choice.EXCLUSIVE, new String[] { DQ_WRITE, DQ_READ}, null);

private StringItem dqStatusText_ = new StringItem("Status", null);


/**

* Creates a new ToolboxMidpDemo.

**/

public ToolboxMidpDemo()

{

display_ = Display.getDisplay(this);

// Note: The KVM-based demo used TabbedPane for the main panel. MIDP has no similar class, so we use a List instead.

}

/**

* Show the main screen.

* Implements abstract method of class Midlet.

**/

protected void startApp()

{

main_.append(SIGN_ON, null);

main_.append(COMMAND_CALL, null);

main_.append(PROGRAM_CALL, null);

main_.append(DATA_QUEUE, null);

main_.append(ABOUT, null);

main_.addCommand(actionExit_);

main_.setCommandListener(this);

display_.setCurrent(main_);

}

// Implements method of interface CommandListener.

public void commandAction(Command action, Displayable dsp)

{

// All 'exit' and 'back' processing is the same.

if (action == actionExit_)

{

destroyApp(false);

notifyDestroyed();

}

else if (action == actionBack_)

{

// Return to main menu.

display_.setCurrent(main_);

}

else if (dsp instanceof List)

{

List current = (List)dsp;

// An action occurred on the main page

if (current == main_)

{

int idx = current.getSelectedIndex();

switch (idx)

{

case 0: // SignOn

showSignonForm();

break;

case 1: // CommandCall

showCmdForm();

break;

case 2: // ProgramCall

showPgmForm();

break;

case 3: // DataQueue

showDqForm();

break;

case 4: // About

showAboutForm();

break;

default: // None of the above

feedback("Internal error: Unhandled selected index in main: " + idx, AlertType.ERROR);

break;

}

} // current == main

else

feedback("Internal error: The Displayable object is a List but is not main_.", AlertType.ERROR);

} // instanceof List

else if (dsp instanceof Form)

{

Form current = (Form)dsp;

if (current == signonForm_)

{

if (action == actionSignon_)

{

// Create a ToolboxME AS400 object.

system_ = new AS400(signonSystemText_.getString(), signonUidText_.getString(), signonPwdText_.getString(), signonServerText_.getString());

try

{

// Connect to the iSeries.

system_.connect();

// Set the signon status text.

signonStatusText_.setText("Signed on.");

// Display a confirmation dialog that the user is signed on.

feedback("Successfully signed on.", AlertType.INFO, main_);

// Replace the SignOn button with SignOff.

signonForm_.removeCommand(actionSignon_);

signonForm_.addCommand(actionSignoff_);

// Update the ticker.

ticker_.setString("... Signed on to '" + signonSystemText_.getString() + "' as '" + signonUidText_.getString() + "' via '" + signonServerText_.getString() + "' ... ");

}

catch (Exception e)

{

e.printStackTrace();

// Set the signon status text.

signonStatusText_.setText(NOT_SIGNED_ON);

feedback("Signon failed. " + e.getMessage(), AlertType.ERROR);

}

}

else if (action == actionSignoff_)

{

if (system_ == null)

feedback("Internal error: System is null.", AlertType.ERROR);

else

{

try

{

// Disconnect from the iSeries.

system_.disconnect();

system_ = null;

// Set the signon status text.

signonStatusText_.setText(NOT_SIGNED_ON);

// Display a confirmation dialog that the user is no longer signed on.

feedback("Successfully signed off.", AlertType.INFO, main_);

// Replace the SignOff button with SignOn.

signonForm_.removeCommand(actionSignoff_);

signonForm_.addCommand(actionSignon_);

// Update the ticker.

ticker_.setString(NOT_SIGNED_ON);

}

catch (Exception e)

{

feedback(e.toString(), AlertType.ERROR);

e.printStackTrace();

signonStatusText_.setText("Error.");

feedback("Error during signoff.", AlertType.ERROR);

}

}

}

else // None of the above.

{

feedback("Internal error: Action is not recognized.", AlertType.INFO);

}

} // signonForm_

else if (current == cmdcallForm_)

{

if (action == actionRun_)

{

// If the user has not signed on, display an alert.

if (system_ == null)

{

feedback(NOT_SIGNED_ON, AlertType.ERROR);

return;

}

// Get the command the user entered in the wireless device.

String cmdString = cmdText_.getString();

// If the command was not specified, display an alert.

if (cmdString == null || cmdString.length() == 0)

feedback("Specify command.", AlertType.ERROR);

else

{

try

{

// Run the command.

String[] messages = CommandCall.run(system_, cmdString);

StringBuffer status = new StringBuffer("Command completed with ");

// Check to see if their are any messages.

if (messages.length == 0)

{

status.append("no returned messages.");

cmdMsgText_.setText(null);

cmdStatusText_.setText("Command completed successfully.");

}

else

{

if (messages.length == 1)

status.append("1 returned message.");

else

status.append(messages.length + " returned messages.");

// If there are messages, display only the first message.

cmdMsgText_.setText(messages[0]);

cmdStatusText_.setText(status.toString());

}

repaint();

}

catch (Exception e)

{

feedback(e.toString(), AlertType.ERROR);

e.printStackTrace();

feedback("Error when running command.", AlertType.ERROR);

}

}

}

else if (action == actionClear_)

{

// Clear the command text and messages.

cmdText_.setString("");

cmdMsgText_.setText(null);

cmdStatusText_.setText(null);

repaint();

}

else // None of the above.

{

feedback("Internal error: Action is not recognized.", AlertType.INFO);

}

} // cmdcallForm_

else if (current == pgmcallForm_)

{

if (action == actionRun_)

{

// If the user is not signed on before doing a program call, display an alert.

if (system_ == null)

{

feedback(NOT_SIGNED_ON, AlertType.ERROR);

return;

}

pgmMsgText_.setText(null);

// See the PCML example in the Toolbox programmer's guide.

String pcmlName = "qsyrusri.pcml"; // The PCML file we want to use.

String apiName = "qsyrusri";

// Create a hashtable that contains the input parameters for the program call.

Hashtable parmsToSet = new Hashtable(2);

parmsToSet.put("qsyrusri.receiverLength", "2048");

parmsToSet.put("qsyrusri.profileName", signonUidText_.getString().toUpperCase());

// Create a string array that contains the output parameters to retrieve.

String[] parmsToGet = { "qsyrusri.receiver.userProfile",

"qsyrusri.receiver.previousSignonDate",

"qsyrusri.receiver.previousSignonTime",

"qsyrusri.receiver.daysUntilPasswordExpires"};

// A string array containing the descriptions of the parameters to display.

String[] displayParm = { "Profile", "Last signon Date", "Last signon Time", "Password Expired (days)"};

try

{

// Run the program.

String[] valuesToGet = ProgramCall.run(system_, pcmlName, apiName, parmsToSet, parmsToGet);

// Create a StringBuffer and add each of the parameters we retreived.

StringBuffer txt = new StringBuffer();

txt.append(displayParm[0] + ": " + valuesToGet[0] + "\n");

char[] c = valuesToGet[1].toCharArray();

txt.append(displayParm[1] + ": " + c[3]+c[4]+"/"+c[5]+c[6]+"/"+c[1]+c[2] + "\n");

char[] d = valuesToGet[2].toCharArray();

txt.append(displayParm[2] + ": " + d[0]+d[1]+":"+d[2]+d[3] + "\n");

txt.append(displayParm[3] + ": " + valuesToGet[3] + "\n");

// Set the displayable text of the program call results.

pgmMsgText_.setText(txt.toString());

StringBuffer status = new StringBuffer("Program completed with ");

if (valuesToGet.length == 0)

{

status.append("no returned values.");

feedback(status.toString(), AlertType.INFO);

}

else

{

if (valuesToGet.length == 1)

status.append("1 returned value.");

else

status.append(valuesToGet.length + " returned values.");

feedback(status.toString(), AlertType.INFO);

}

}

catch (Exception e)

{

feedback(e.toString(), AlertType.ERROR);

e.printStackTrace();

feedback("Error when running program.", AlertType.ERROR);

}

}

else if (action == actionClear_)

{

// Clear the program call results.

pgmMsgText_.setText(null);

repaint();

}

} // pgmcallForm_

else if (current == dataqueueForm_) // DataQueue

{

if (action == actionGo_)

{

// If the user has not signed on before performing Data Queue actions, display an alert.

if (system_ == null)

{

feedback(NOT_SIGNED_ON, AlertType.ERROR);

return;

}

// Create a library to create the data queue in.

try

{

CommandCall.run(system_, "CRTLIB FRED");

}

catch (Exception e)

{

}

// Run a command to create a data queue.

try

{

CommandCall.run(system_, "CRTDTAQ FRED/MYDTAQ MAXLEN(2000)");

}

catch (Exception e)

{

feedback("Error when creating data queue. " + e.getMessage(), AlertType.WARNING);

}

try

{

// See which action was selected (Read or Write).

if (dqReadOrWrite_.getString(dqReadOrWrite_.getSelectedIndex()).equals(DQ_WRITE))

{

// Write

dqOutputText_.setText(null);

// Get the text from the wireless device input to be written to the data queue.

if (dqInputText_.getString().length() == 0)

dqStatusText_.setText("No data specified.");

else

{

// Write to the data queue.

DataQueue.write(system_, "/QSYS.LIB/FRED.LIB/MYDTAQ.DTAQ", dqInputText_.getString().getBytes() );

dqInputText_.setString(null);

// Display the status.

dqStatusText_.setText("The 'write' operation completed.");

}

}

else // Read

{

// Read from the data queue.

byte[] b = DataQueue.readBytes(system_, "/QSYS.LIB/FRED.LIB/MYDTAQ.DTAQ");

// Determine if the data queue contained entries or not and display the appropriate message.

if (b == null)

{

dqStatusText_.setText("No dataqueue entries are available.");

dqOutputText_.setText(null);

}

else if (b.length == 0)

{

dqStatusText_.setText("Dataqueue entry has no data.");

dqOutputText_.setText(null);

}

else

{

dqStatusText_.setText("The 'read' operation completed.");

dqOutputText_.setText(new String(b));

}

}

repaint();

}

catch (Exception e)

{

e.printStackTrace();

feedback(e.toString(), AlertType.ERROR);

feedback("Error when running command. " + e.getMessage(), AlertType.ERROR);

}

} // actionGo_

else if (action == actionClear_)

{

// Clear the data queue form.

dqInputText_.setString("");

dqOutputText_.setText(null);

dqReadOrWrite_.setSelectedFlags(new boolean[] { true, false});

dqStatusText_.setText(null);

repaint();

}

else // None of the above.

{

feedback("Internal error: Action is not recognized.", AlertType.INFO);

}

} // dataqueueForm_

else if (current == aboutForm_) // "About".

{

// Should never reach here, since the only button is "Back".

} // None of the above.

else

feedback("Internal error: Form is not recognized.", AlertType.ERROR);

} // instanceof Form

else

feedback("Internal error: Displayable object not recognized.", AlertType.ERROR);

}



/**

* Displays the "About" form.

**/

private void showAboutForm()

{

// If the about form is null, create and append it.

if (aboutForm_ == null)

{

aboutForm_ = new Form(ABOUT);

aboutForm_.append(new StringItem(null, "This is a MIDP sample application that uses the Toolbox Micro Edition (ToolboxME)."));

aboutForm_.addCommand(actionBack_);

aboutForm_.setCommandListener(this);

}

display_.setCurrent(aboutForm_);

}



/**

* Displays the "SignOn" form.

**/

private void showSignonForm()

{

// Create the signon form.

if (signonForm_ == null)

{

signonForm_ = new Form(SIGN_ON);

signonForm_.append(signonSystemText_);

signonForm_.append(signonUidText_);

signonForm_.append(signonPwdText_);

signonForm_.append(signonServerText_);

signonForm_.append(signonStatusText_);

signonForm_.addCommand(actionBack_);

signonForm_.addCommand(actionSignon_);

signonForm_.setCommandListener(this);

signonForm_.setTicker(ticker_);

}

display_.setCurrent(signonForm_);

}



/**

* Displays the "CommandCall" form.

**/

private void showCmdForm()

{

// Create the command call form.

if (cmdcallForm_ == null)

{

cmdcallForm_ = new Form(COMMAND_CALL);

cmdcallForm_.append(cmdText_);

cmdcallForm_.append(cmdMsgText_);

cmdcallForm_.append(cmdStatusText_);

cmdcallForm_.addCommand(actionBack_);

cmdcallForm_.addCommand(actionClear_);

cmdcallForm_.addCommand(actionRun_);

cmdcallForm_.setCommandListener(this);

cmdcallForm_.setTicker(ticker_);

}

display_.setCurrent(cmdcallForm_);

}



/**

* Displays the "ProgramCall" form.

**/

private void showPgmForm()

{

// Create the program call form.

if (pgmcallForm_ == null)

{

pgmcallForm_ = new Form(PROGRAM_CALL);

pgmcallForm_.append(new StringItem(null, "This calls the Retrieve User Information (QSYRUSRI) API, and returns information about the current user profile."));

pgmcallForm_.append(pgmMsgText_);

pgmcallForm_.addCommand(actionBack_);

pgmcallForm_.addCommand(actionClear_);

pgmcallForm_.addCommand(actionRun_);

pgmcallForm_.setCommandListener(this);

pgmcallForm_.setTicker(ticker_);

}

display_.setCurrent(pgmcallForm_);

}


/**

* Displays the "DataQueue" form.

**/

private void showDqForm()

{

// Create the data queue form.

if (dataqueueForm_ == null)

{

dataqueueForm_ = new Form(DATA_QUEUE);

dataqueueForm_.append(dqInputText_);

dataqueueForm_.append(dqOutputText_);

dataqueueForm_.append(dqReadOrWrite_);

dataqueueForm_.append(dqStatusText_);

dataqueueForm_.addCommand(actionBack_);

dataqueueForm_.addCommand(actionClear_);

dataqueueForm_.addCommand(actionGo_);

dataqueueForm_.setCommandListener(this);

dataqueueForm_.setTicker(ticker_);

}

display_.setCurrent(dataqueueForm_);

}


private void feedback(String text, AlertType type)

{

feedback(text, type, display_.getCurrent());

}

/**

* This method is used to create a dialog and display feedback information using an Alert to the user.

**/

private void feedback(String text, AlertType type, Displayable returnToForm)

{

System.err.flush();

System.out.flush();

Alert alert = new Alert("Alert", text, null, type);

if (type == AlertType.INFO)

alert.setTimeout(3000); // milliseconds

else

alert.setTimeout(Alert.FOREVER); // Require user to dismiss the alert.

display_.setCurrent(alert, returnToForm);

}


// Force a repaint of the current form.

private void repaint()

{

Alert alert = new Alert("Updating display ...", null, null, AlertType.INFO);

alert.setTimeout(1000); // milliseconds

display_.setCurrent(alert, display_.getCurrent());

}



/**

* Time to pause, free any space we don't need right now.

* Implements abstract method of class Midlet.

**/

protected void pauseApp()

{

display_.setCurrent(null);

}



/**

* Destroy must cleanup everything.

* Implements abstract method of class Midlet.

**/

protected void destroyApp(boolean unconditional)

{

// Disconnect from the iSeries if the Midlet is being destroyed or exited.

if (system_ != null)

{

try

{

system_.disconnect();

}

catch (Exception e)

{

}

}

}

}



Sample ToolboxME Application Using the JdbcMe Classes

/////////////////////////////////////////////////////////////////////////

//

// This source is an example of using the IBM Toolbox for Java 2 Micro Edition

// package classes, which allow you to easily build wireless applications.

// IBM grants you a nonexclusive license to use this as an example

// from which you can generate similar function tailored to

// your own specific needs.

//

// This sample code is provided by IBM for illustrative purposes

// only. These examples have not been thoroughly tested under all

// conditions. IBM, therefore, cannot guarantee or imply

// reliability, serviceability, or function of these programs.

//

// All programs contained herein are provided to you "AS IS"

// without any warranties of any kind. The implied warranties of

// merchantablility and fitness for a particular purpose are

// expressly disclaimed.

//

// IBM Toolbox for Java 2 Micro Edition

// (C) Copyright IBM Corp. 2002

// All rights reserved.

// US Government Users Restricted Rights -

// Use, duplication, or disclosure restricted

// by GSA ADP Schedule Contract with IBM Corp.

//

/////////////////////////////////////////////////////////////////////////

import java.sql.*; // SQL Interfaces provided by JdbcMe

import com.ibm.as400.micro.*; // JdbcMe implementation

import java.awt.*;

import java.awt.event.*;

import java.io.*;

import javax.microedition.io.*; // Part of the CLDC specification

import de.kawt.*; // Part of the CLDC specification

class DemoConstants

{

// These constants are actually used mainly by the demo

// for the JDBC driver. The Jdbc and JDBC application

// creator IDs ( http://www.palmos.com/dev )

// are reserved at palm computing.

public static final int demoAppID = 0x4a444243; // JDBC

// Make the dbCreator something else so that the

// user can actually see the Palm DB seperately from

// the JdbcDemo application.

public static final int dbCreator = 0x4a444231; // JDB1

public static final int dbType = 0x4a444231; // JDB1

}

/**

* Little configuration dialog box to display the

* current connections/statements, the

* URL being used, user id and password

*/

class ConfigurationDialog extends Dialog implements ActionListener

{

TextField data;

ConfigurationDialog(Frame w)

{

super(w, "Configuration");

// Show/Modify current URL connection

data = new TextField(JdbcDemo.mainFrame.jdbcPanel.url);

add("Center", data);

// Ok button.

Panel panel = new Panel();

Button button = new Button("Ok");

button.addActionListener(this);

panel.add(button);

add("South", panel);

pack();

}

public void actionPerformed(ActionEvent e)

{

JdbcDemo.mainFrame.jdbcPanel.url = data.getText();

data = null;

setVisible(false);

}

}

/**

* Little configuration dialog box to display the

* current connections/statements, the

* URL being used, user id and password

*/

class MultiChoiceDialog extends Dialog implements ActionListener

{

Choice task;

ActionListener theListener;

MultiChoiceDialog(Frame w, String title, String prompt, String choices[], ActionListener it)

{

super(w, title);

theListener = it;

// Show/Modify current URL connection

Label txt = new Label(prompt);

add("West", txt);

task = new Choice();

for (int i=0; i<choices.length; ++i)

{

task.add(choices[i]);

}

task.select(0);

add("Center", task);

// Ok button.

Panel panel = new Panel();

Button button = new Button("Ok");

button.addActionListener(this);

panel.add(button);

button = new Button("Cancel");

button.addActionListener(this);

panel.add(button);

add("South", panel);

pack();

}

/**

* Determine the action performed.

**/

public void actionPerformed(ActionEvent e)

{

int choice = task.getSelectedIndex();

setVisible(false);

if (e.getActionCommand().equals("Ok"))

{

if (theListener != null)

{

ActionEvent ev = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, task.getItem(choice));

theListener.actionPerformed(ev);

}

task = null;

}

else

{

// No-op

}

}

}

/**

* The JdbcPanel is the main panel of the application.

* It displays the current connection and statement

* at the top.

* A text field for entering SQL statements next.

* A Results field for displaying each column of data

* or results.

* An task list and a 'go' button so that different

* tasks can be tried.

*/

class JdbcPanel extends Panel implements ActionListener

{

public final static int TASK_EXIT = 0;

public final static int TASK_NEW = 1;

public final static int TASK_CLOSE = 2;

public final static int TASK_EXECUTE = 3;

public final static int TASK_PREV = 4;

public final static int TASK_NEXT = 5;

public final static int TASK_CONFIG = 6;

public final static int TASK_TOPALMDB = 7;

public final static int TASK_FROMPALMDB = 8;

public final static int TASK_SETAUTOCOMMIT= 9;

public final static int TASK_SETISOLATION = 10;

public final static int TASK_COMMIT = 11;

public final static int TASK_ROLLBACK = 12;


// JDBC objects.

java.sql.Connection connObject = null;

Statement stmtObject = null;

ResultSet rs = null;

ResultSetMetaData rsmd = null;

String lastErr = null;

String url = null;

Label connection = null;

Label statement = null;

TextField sql = null;

List data = null;

final Choice task;

/**

* Build the GUI.

*/

public JdbcPanel()

{

// The JDBC URL

// Make sure to edit the following line so that it correctly specifies the

// the MEServer and the iSeries server to which you want to connect.

url = "jdbc:as400://mySystem;user=myUidl;password=myPwd;meserver=myMEServer;";

Panel p1left = new Panel();

p1left.setLayout(new BorderLayout());

connection = new Label("None");

p1left.add("West", new Label("Conn:"));

p1left.add("Center", connection);

Panel p1right = new Panel();

p1right.setLayout(new BorderLayout());

statement = new Label("None");

p1right.add("West", new Label("Stmt:"));

p1right.add("Center", statement);

Panel p1 = new Panel();

p1.setLayout(new GridLayout(1,2));

p1.add(p1left);

p1.add(p1right);

Panel p2 = new Panel();

p2.setLayout(new BorderLayout());

p2.add("North", new Label("Sql:"));

sql = new TextField(25);

sql.setText("select * from QIWS.QCUSTCDT"); // Default query

p2.add("Center", sql);

Panel p3 = new Panel();

p3.setLayout(new BorderLayout());

data = new List();

data.add("No Results");

p3.add("North", new Label("Results:"));

p3.add("Center", data);

Panel p4 = new Panel();

task = new Choice();

task.add("Exit"); // TASK_EXIT

task.add("New"); // TASK_NEW

task.add("Close"); // TASK_CLOSE

task.add("Execute"); // TASK_EXECUTE

task.add("Prev"); // TASK_PREV

task.add("Next"); // TASK_NEXT

task.add("Config"); // TASK_CONFIGURE

task.add("RS to PalmDB"); // TASK_TOPALMDB

task.add("Query PalmDB"); // TASK_FROMPALMDB

task.add("Set AutoCommit"); // TASK_SETAUTOCOMMIT

task.add("Set Isolation"); // TASK_SETISOLATION

task.add("Commit"); // TASK_COMMIT

task.add("Rollback"); // TASK_ROLLBACK

task.select(TASK_EXECUTE); // Start off here.

p4.add("West", task);

Button b = new Button("Go");

b.addActionListener(this);

p4.add("East", b);

Panel prest = new Panel();

prest.setLayout(new BorderLayout());

prest.add("North", p2);

prest.add("Center", p3);

Panel pall = new Panel();

pall.setLayout(new BorderLayout());

pall.add("North", p1);

pall.add("Center", prest);

setLayout(new BorderLayout());

add("Center", pall);

add("South", p4);

}

/**

* Do a task based on whichever task is

* currently selected in the task list.

*/

public void actionPerformed(ActionEvent e)

{

if (e.getSource() instanceof MultiChoiceDialog)

{

String cmd = e.getActionCommand();

processExtendedCommand(cmd);

return;

}

switch (task.getSelectedIndex())

{

case TASK_EXIT:

System.exit(0);

break;

case TASK_NEW:

JdbcPanel.this.goNewItems();

break;

case TASK_PREV:

JdbcPanel.this.goPrevRow();

break;

case TASK_NEXT:

JdbcPanel.this.goNextRow();

break;

case TASK_EXECUTE:

if (connObject == null || stmtObject == null)

JdbcPanel.this.goNewItems();

JdbcPanel.this.goExecute();

break;

case TASK_CONFIG:

JdbcPanel.this.goConfigure();

break;

case TASK_CLOSE:

JdbcPanel.this.goClose();

break;

case TASK_TOPALMDB:

if (connObject == null || stmtObject == null)

JdbcPanel.this.goNewItems();

JdbcPanel.this.goResultsToPalmDB();

break;

case TASK_FROMPALMDB:

JdbcPanel.this.goQueryFromPalmDB();

break;

case TASK_SETAUTOCOMMIT:

JdbcPanel.this.goSetAutocommit();

break;

case TASK_SETISOLATION:

JdbcPanel.this.goSetIsolation();

break;

case TASK_COMMIT:

JdbcPanel.this.goTransact(true);

break;

case TASK_ROLLBACK:

JdbcPanel.this.goTransact(false);

break;

default :

{

Dialog dialog = new FeedbackDialog(JdbcDemo.mainFrame, "Error", "Task not implemented");

dialog.show();

dialog = null;

}

}

}

public void processExtendedCommand(String cmd)

{

try

{

if (cmd.equals("true"))

{

connObject.setAutoCommit(true);

return;

}

if (cmd.equals("false"))

{

connObject.setAutoCommit(false);

return;

}

if (cmd.equals("read uncommitted"))

{

connObject.setTransactionIsolation(java.sql.Connection.TRANSACTION_READ_UNCOMMITTED);

return;

}

if (cmd.equals("read committed"))

{

connObject.setTransactionIsolation(java.sql.Connection.TRANSACTION_READ_COMMITTED);

return;

}

if (cmd.equals("repeatable read"))

{

connObject.setTransactionIsolation(java.sql.Connection.TRANSACTION_REPEATABLE_READ);

return;

}

if (cmd.equals("serializable"))

{

connObject.setTransactionIsolation(java.sql.Connection.TRANSACTION_SERIALIZABLE);

return;

}

throw new IllegalArgumentException("Invalid command: " + cmd);

}

catch (Exception e)

{

JdbcDemo.mainFrame.exceptionFeedback(e);

return;

}

}

/**

* Perform commit or rollback processing.

*/

public void goTransact(boolean commit)

{

if (connObject == null)

{

FeedbackDialog dialog = new FeedbackDialog(JdbcDemo.mainFrame, "Skip", "Connection not allocated");

dialog.show();

dialog = null;

return;

}

try

{

if (commit)

connObject.commit();

else

connObject.rollback();

}

catch (Exception e)

{

JdbcDemo.mainFrame.exceptionFeedback(e);

}

}

/**

* Prompt the user for setting the autocommit value

* Real work handled by the actionPerformed method

* calling processExtendedCommand().

*/

public void goSetAutocommit()

{

if (connObject == null)

{

FeedbackDialog dialog = new FeedbackDialog(JdbcDemo.mainFrame, "Skip", "Connection not allocated");

dialog.show();

dialog = null;

return;

}

try

{

String currentValue;

if (connObject.getAutoCommit())

currentValue = "Now: true";

else

currentValue = "Now: false";

Dialog dialog = new MultiChoiceDialog(JdbcDemo.mainFrame, "Set Autocommit", currentValue, new String[]{ "true", "false"}, this);

dialog.show();

dialog = null;

}

catch (Exception e)

{

JdbcDemo.mainFrame.exceptionFeedback(e);

}

}

/**

* Prompt the user for setting the isolation level,

* real work handled by the actionPerformed() method

* calling processExtendedCommand().

*/

public void goSetIsolation()

{

if (connObject == null)

{

FeedbackDialog dialog = new FeedbackDialog(JdbcDemo.mainFrame, "Skip", "Connection not allocated");

dialog.show();

dialog = null;

return;

}

try

{

int level = connObject.getTransactionIsolation();

String currentLevel;

switch (level)

{

case java.sql.Connection.TRANSACTION_READ_UNCOMMITTED:

currentLevel = "Now: read uncommitted";

break;

case java.sql.Connection.TRANSACTION_READ_COMMITTED:

currentLevel = "Now: read committed";

break;

case java.sql.Connection.TRANSACTION_REPEATABLE_READ:

currentLevel = "Now: repeatable read";

break;

case java.sql.Connection.TRANSACTION_SERIALIZABLE:

currentLevel = "Now: serializable";

break;

default : {

currentLevel = "error";

}

}

Dialog dialog = new MultiChoiceDialog(JdbcDemo.mainFrame,

"Set Isolation Level",

currentLevel,

new String[]{ "read uncommitted", "read committed", "repeatable read","serializable"},

this);

dialog.show();

dialog = null;

}

catch (Exception e)

{

JdbcDemo.mainFrame.exceptionFeedback(e);

}

}

/**

* Create a new connection or statement.

* Only one connection and statement is currently

* supported.

*/

public void goNewItems()

{

if (connObject != null || stmtObject != null)

{

FeedbackDialog dialog = new FeedbackDialog(JdbcDemo.mainFrame, "Skip", "Conn/Stmt already allocated");

dialog.show();

dialog = null;

}

if (connObject == null)

{

try

{

connObject = DriverManager.getConnection(url);

//connection.setText(Integer.toString(((JdbcMeConnection)connObject).getId()));

connection.repaint();

}

catch (Exception e)

{

JdbcDemo.mainFrame.exceptionFeedback(e);

return;

}

}

if (stmtObject == null)

{

try

{

try

{

stmtObject = connObject.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);

}

catch (Exception e)

{

// Try again... DB2 NT version 6.1 doesn't support

// Scollable result sets, so we'll assume other

// JDBC 2.0 databases don't either. We'll attempt

// to create another.

try

{

stmtObject = connObject.createStatement();

}

catch (Exception ex)

{

// If the second try failed, rethrow the

// first exception. Its probably

// a more meaninful error.

throw e;

}

FeedbackDialog dialog = new FeedbackDialog(JdbcDemo.mainFrame, "2nd try worked", "Non-scrollable result set");

dialog.show();

dialog = null;

}


statement.repaint();

}

catch (Exception e)

{

JdbcDemo.mainFrame.exceptionFeedback(e);

return;

}

}

}


/**

* Close the statement and connection.

**/

public void goClose()

{

// Close the statement.

if (stmtObject != null)

{

if (rs != null)

{

try

{

rs.close();

}

catch (Exception e)

{

}

rs = null;

rsmd = null;

}

try

{

stmtObject.close();

}

catch (Exception e)

{

}

stmtObject = null;

statement.setText("None");

statement.repaint();

}

// Clost the connection.

if (connObject != null)

{

try

{

connObject.close();

}

catch (Exception e)

{

}

connObject = null;

connection.setText("None");

connection.repaint();

}

data.removeAll();

data.add("No Results");

data.repaint();

sql.repaint();

return;

}

/**

* display the configuration dialog.

**/

public void goConfigure()

{

// Note there is no model dialog support in KAWT, this only

// works because the data to be changed (url) is set before

// this dialog is used, and the user cannot access the

// main frame while this is up on the palm (i.e. all dialogs

// in Kawt are modal).

ConfigurationDialog dialog = new ConfigurationDialog(JdbcDemo.mainFrame);

dialog.show();

dialog = null;

}

/**

* Execute the specified query.

**/

public void goExecute()

{

// Get the currently selected statement.

try

{

if (rs != null)

rs.close();

rs = null;

rsmd = null;

boolean results = stmtObject.execute(sql.getText());

if (results)

{

rs = stmtObject.getResultSet();

rsmd = rs.getMetaData();

// Show the first row

goNextRow();

}

else

{

data.removeAll();

data.add(stmtObject.getUpdateCount() + " rows updated");

data.repaint();

}

}

catch (Exception e)

{

JdbcDemo.mainFrame.exceptionFeedback(e);

}

}


/**

* Move to the next row in the result set.

**/

public void goNextRow()

{

try

{

if (rs == null || rsmd == null)

return;

int count = rsmd.getColumnCount();

int i;

data.removeAll();

if (!rs.next())

data.add("End of data");

else

{

for (i=1; i>=count; ++i)

{

data.add(rs.getString(i));

}

}

data.repaint();

}

catch (Exception e)

{

JdbcDemo.mainFrame.exceptionFeedback(e);

}

}


/**

* Move to the previous row in the result set.

**/

public void goPrevRow()

{

try

{

if (rs == null || rsmd == null)

return;

int count = rsmd.getColumnCount();

int i;

data.removeAll();

if (!rs.previous())

data.add("Start of data");

else

{

for (i=1; i<=count; ++i)

{

data.add(rs.getString(i));

}

}

data.repaint();

}

catch (Exception e)

{

JdbcDemo.mainFrame.exceptionFeedback(e);

}

}

/**

* Perform a query and store the results in the local devices database

**/

public void goResultsToPalmDB()

{

try

{

if (stmtObject == null)

{

FeedbackDialog dialog = new FeedbackDialog(JdbcDemo.mainFrame, "Skip", "No Statement");

dialog.show();

dialog = null;

return;

}

boolean results = ((JdbcMeStatement)stmtObject).executeToOfflineData(sql.getText(), "JdbcResultSet", DemoConstants.dbCreator, DemoConstants.dbType);

if (!results)

{

FeedbackDialog dialog = new FeedbackDialog(JdbcDemo.mainFrame, "No Data", "Not a query");

dialog.show();

dialog = null;

return;

}

data.removeAll();

data.add("Updated Palm DB 'JdbcResultSet'");

data.repaint();

}

catch (Exception e)

{

JdbcDemo.mainFrame.exceptionFeedback(e);

}

}


/**

* Perform a query from the database that resides on the palm device.

**/

public void goQueryFromPalmDB()

{

try

{

if (rs != null)

{

rs.close();

rs = null;

}

rs = new JdbcMeOfflineResultSet ("JdbcResultSet", DemoConstants.dbCreator, DemoConstants.dbType);

rsmd = rs.getMetaData();

// If we want to debug some output, this

// method can be used to dump the contents

// of the PalmDB represented by the result set

// (Uses System.out so its mostly useful in

// the Palm emulator when debugging your

// applications.

// ((JdbcMeOfflineResultSet)rs).dumpDB(true);

// show the first row.

goNextRow();

}

catch (SQLException e)

{

JdbcDemo.mainFrame.exceptionFeedback(e);

}

}

}

public class JdbcDemo extends Frame

{

/** An ActionListener that ends the application. Only

* one is required, and can be reused

*/

private static ActionListener exitActionListener = null;

/**

* The main application in this process.

*/

static JdbcDemo mainFrame = null;

JdbcPanel jdbcPanel = null;

public static ActionListener getExitActionListener()

{

if (exitActionListener == null)

{

exitActionListener = new ActionListener()

{

public void actionPerformed(ActionEvent e)

{

System.exit(0);

}

};

}

return exitActionListener;

}

/**

* Demo Constructor

**/

public JdbcDemo()

{

super("Jdbc Demo");

setLayout(new BorderLayout());

jdbcPanel = new JdbcPanel();

add("Center", jdbcPanel);

addWindowListener(new WindowAdapter()

{

public void windowClosing(WindowEvent e)

{

System.exit(0);

}

});

setSize(200,300);

pack();

}

public void exceptionFeedback(Exception e)

{

Dialog dialog = new FeedbackDialog(JdbcDemo.mainFrame, e);

dialog.show();

dialog = null;

}

/**

* Main method.

**/

public static void main(String args[])

{

try

{

mainFrame = new JdbcDemo();

mainFrame.show();

mainFrame.jdbcPanel.goConfigure();

}

catch (Exception e)

{

System.exit(1);

}

}

}

Figure 7. Coding Examples