A "Rich" Alternative for System i GUI Development
Elena Lower wrote an article published this month in System iNEWS, ProVip (May 2007), "5 Tips for Integrating Java with System i Resources". RCP DtaQ Demo project will benefit from the first tip, Connection to the System i. Lets apply it:

These changes give RCP DtaQ Demo a logon dialog consistent with the graphical appearance of the application and is more functional. The user can cancel the logon without ending the application and later call for the logon dialog through the file menu.
This is how it is done.
RCP DtaQ Demo violates Elena's advice by using the AS400 default sign-in dialog. Worse the user is forced to either sign in or cancel and abort the program. In something simple like this, something that needs a System i connection to work, requiring a successful logon before allowing the application to continue is reasonable. This would not work well if the user had to do something, say set some preferences (that is a hint about future development), before logging on.
Three classes will be added to the dataqueue package and other classes will be changed in base package.
LogonDialog
Eclipse defines a Dialog as " … specialized window used for narrow-focused communication with the user. Dialogs are usually modal…" In this case the focus is to get the logon information from the user. The logon dialog is shown.
There is no wizard to build a dialog in Eclipse. This is the code for LogonDialog.java.
When the OK button is pressed the logon information is validated. Errors are shown in the error status line. The OK button won't exit the Dialog unless the information is validated. The CANCEL button function isn't changed.
ListenForModelOutput
The guts of this class were taken from the Activator.start() method. The Activator class, startup() method is an excellent place to create application wide objects, like the AS400 and data queue objects. The problem is that the User Interface isn't running. Without the user interface the program can't get the logon information from the user.
This is the code for ListenForModelOutput.java.
This class
- activates the Dialog to get the user information
- if the Dialog information is validated then
-- start the model output data queue listener
-- prevent the user from executing the logon dialog again
The AS400 object is created in Activator.startup(). That made it easy to use the default dialog to get the logon information right there.
After the change the AS400 object is created in Activator's constructor. However it can't be used until the system, user and password are provided. The system, user and password can't be provided until the UI is running. So the AS400 object is created without a system, user or password.
The run() method of ListenForModelOutput first calls LogonDialog to get the system, user and password. LogonDialog will only return OK if the system, user and password are validated. To do this is sets these values in the AS400 object. With the validated information then it creates and starts the data queues, work that was previously done in Activator.startup().
Comment out this line of code in ListenForModelOutput.run() which prevents the user from trying to log one once the data queue is running.
LogonAction.getDefault().setEnabled(false);
ListenForModelOutput will be ready to test before the LogonAction class is ready. You will have an error until that class is available, that is why this line is commented out.
Modify ApplicationWorkbenchWindowAdvisor to call ListenForModelOutput
Override the ApplicationWorkbenchWindowAdvisor.postWindowOpen() method to call ListenForModelOutput.
The postWindowOpen() method is called after the UI is stared and the application window is created or restored, but before the application starts. At this point the UI is up and running but the application hasn't done anything. Here is the place to get the system, user and password and then start the model output data queues and its listeners.
Put the cursor in the body of the class between methods, press Ctrl-space, and select postWindowOpen(). This code, except for the last line, will be added:
@Override
public void postWindowOpen() {
// TODO Auto-generated method stub
super.postWindowOpen();
// you add the following line
ListenForModelOutput.run();
}
Add the call to the run() method of ListenForModelOutput.
Modify the Activator class to remove the code now in ListenForModelOutput
Comment out everything in the start() method except the call to the super method. It will look like this:
public void start(BundleContext context) throws Exception {
super.start(context);
}
Remove the method isAs400Connected().
To prevent the AS400 default logon dialog add this snippet to the constructor after instantiating the AS400 object:
try {
as400.setGuiAvailable(false);
} catch( PropertyVetoException e) {}
See Elena Lowery's explanation.
Remove the method isAs400Connected().
Activator.java is creating the AS400 object but other classes are validating it and using it to create the Thread or KeyedDataQueue objects. So the AS400 object needs to be accessable from outside Activator and Activator needs methods to set the Thread and KeyedDataQueue objects since they are used in the stop() method.
Add these methods to Activator.java.
/**
* Return AS400 object.
* @return AS400 object created during startup.
*/
public AS400 getAS400() {
return as400;
}
/**
* Thread to listen to model output data queue is create outside
* Activator class however Activator needs access in order to end
* the thread in a controlled way. Use this method to call back
* into Activator to set the Thread after it is created.
*
* @param Thread which has been started to listen to model output
* data queue.
*/
public void setMdloDqThread( Thread _mdloDqThread ) {
mdloDqThread = _mdloDqThread;
}
/**
* Model output data queue is create outside
* Activator class however Activator needs access in order to end
* the thread in a controlled way. Use this method to call back
* into Activator to set the data queue after it is created.
*
* @param Model output data queue to which Thread listens.
*/
public void setModelOutputDataQueue( ModelOutputDataQueue _mdloDq ) {
mdloDq = _mdloDq;
}
Well, that is quite a bit of hacking to the Activator which was first generated by the wizard. This is Activator.java with most of the original code commented out.
Now test RCP DtaQ Demo
It will work as before except
1. the log on dialog is handled by the applications UI, not the AS400 object, and
2. the cancel button does not end the application.
Messages from the sign on process, like "invalid user id" or "invalid password" appear in the message box of the dialog.
What about the Cancel button?
The cancel button of LogonDialog does not end the application. The Thread which listens to the model output data queue can't be started until the signon information is validated. If Cancel is allowed, does not terminate the application, then the application needs to provide the user a means to log on and start the data queue Thread.
To meet this need an action named "Logon" will be added to the File menu.

Basically the Logon action will do the same thing as postWindowOpen() method in the class ApplicationWorkbenchWindowAdvisor. The Logon action lets the user execute the method
ListenForModelOutput.run()
on demand. This method is called after the application window is opened. The action provides the means to re-execute this code if the user cancels the log on.
Create the Action
The Action is LogonAction.java, it is very short. The XML edited by PDE does the heavy lifting.
The run() method checks to see if the application is already listening to the model output data queue by calling
ListenForModelOutput.isListening()
If the application is connected then run() will not execute
The method getDefault() returns the instance of LogonAction to the caller. This is specifically for the line that was commented out in ListenForModelOutput.run() so that it could be tested.
LogonAction.getDefault().setEnabled(false);
This line of code calls back into LogonAction from ListenForModelOutput and disables the Action at the end of the run() method, when the connection is made and the Thread has been started to listen to the model output data queues.
Go back to ListenForModelOutput.run() and remove the comment, that is enable the code.
Use PDE to add an ActionSet extension point and define the logon action
- Open PDE (one way is to double click MANIFEST.MF in the META-INF folder.
- Go to the extention tab.
- Press the Add button.
- In the "Extension Point filter area" start typing "org.eclipse.ui.actionSets". When you see this on the list select it and press Finish. Don't select the template "Hello World" action.

- right click the new extension (...actionSets)
- select New
- select ActionSet, accept the default name actionSet1

These are the Extention Element Details.

You will need to add
- the icon (don't skip this, the Action won't work without something here)
- the tool tip
- the Action class, LogonAction.java
Add the Logon Action to the File menu
Edit ApplicationActionBarAdvisor.java to add the Logon action to the File menu.
Copy
private IWorkbenchAction aboutAction;
then change the copied line to
private IWorkbenchAction logonAction;
Instantiate logonAction, add a tooltip and register it.
logonAction = new LogonAction(window);
logonAction.setToolTipText(
"Log onto System i and start listenting to model output data queue.");
register(logonAction);
The tooltip seems to be required. Sometimes the Action won't work if one is not provided.
Add logonAction to the File menu. Add this line:
fileMenu.add(logonAction);
just above
fileMenu.add(exitAction);.
That is it!
When RCP DtaQ Demo starts the connection dialog is displayed. If the login data is entered and OK is pressed the connection is made and the Thread to read the model output data queue starts.
The File menu has a new option, Logon. If the connection was made Logon is disabled (grayed out). If the connection was not made, the user pressed Cancel, then Logon is enabled and the user can connect the application to the System i.
The final source for the project to date is in the CVS, see
info.billblalock.eclipsercp.dtaqdemo2_311
Posted by Bill Blalock on May 31, 2007 at 7:40 PM | Comments (0)
Typically a rich client does not have console output. The classes brought over from Java data queue 211 write their output to stdout. They were designed for a Java command line or console application. RCP DtaQ Demo will be modified to eliminate the console output and use the rich client user interface (UI) and log file. When finished the application would look like this:

- No console output
- The messages are written to the View (think subfile).
- The status line is used to alert the user (no alerts yet).
- Errors are written to the log file.
Notice the scroll bars at the bottom and the top. Those are automatically generated, they would disappear is the window were just a little larger.
The status line was used to display messages sent by the model in the initial version of RCP DtaQ Demo for the sake of brevity, to postpone the work described here. Those messages should be displayed in the UI. In this version the message will be moved to the View, replacing the constants "One", "Two" and "Three." Alerts will be displayed in the status line. Errors will be logged. The console output will be removed.
You should continue to develop on the project that you created in Java data queues 301.
The source is in the CVS. The project is info.billblalock.eclipsercp.dtaqdemo2_302. I renamed the project so that this version would remain as it is, and be seperate from the code of the prior and future versions. The completed source code for each step in RCP DtaQ Demo can be accessed by version.
Changes to the View class.
Define an ArrayList, a type of Collection, as a class variable. Think of it as a dynamic array. It will hold the lines that are displayed on the view.
private ArrayList tableArray;
Change the getElements() method to return an array of the contents of the ArrayList instead of a String[] of "One", "Two", "Three".
public Object[] getElements(Object parent) {
// return new String[] { "One", "Two", "Three" };
return tableArray.toArray();
}
Instantiate the ArrayList variable in the constructor and add the value of the data queue key to the list.
/**
* The constructor.
*/
public View() {
plugin = this;
tableArray = new ArrayList();
addTableEntry("The key generated for this client is: " +
IpDtaQKey.getDqKey()); }
Create a method to add an entry to the ArrayList. Think of this object as a dynamically allocated array. The parameter passed to the method will add the String as the next element of the array.
/**
* Add a String to ArrayList to be displayed in View.
* @param entry String to add to ArrayList which is displayed in View.
*/
public void addTableEntry( String entry ) {
tableArray.add( entry );
view.refresh();
}
This method is called by a class that adds an element to the View, passing the method a String of what is to be added to the view. After adding the String to the array that is display, the View is refreshed or reconstructed.
A new classes, Messages:
Every class in the dataqueue package which writes to the console, System.out.print() or Exception.printStackTrace(), has to be changed. We could add methods like setStatusLine() plus a logging method to each class. A lot of cut and paste.
setStatusLine() doesn't use anything from ModelDemoOutputDqListener so it can be moved to someplace that makes it easier to reuse. This is similar to putting a RPG procedure into a separate program or a service program instead of cut and pasting it as a sub-routine or sub-proceedure..
Create Messages class in the dataqueue package.
Messages.java is a convenience class to make it easy to write messages to the status line and log. It is set up so the methods can be accessed statically. For example
Messages.setStatusline("Hello, I am the status line.");
from any class in the project. It is the first new class that has been created for RCP DtaQ Demo, that is why the link.
Move setStatusline() method from ModelDemoOutputDqListener into Messages class and change from private to static public. The message method can be called anywhere by Messages.setStatusline(message). This eliminates cut and pasting the same method into each class which uses it.
The Messages class needs two more methods. One method writes an error message to the status line. The other writes a log entry.
This writes an error status to the status line. An error status will stay on the status line until it is removed by another setErrorStatus().
/**
* Set error status line of RCP with this message.
* @param message String to be placed on status line, set to
* null to clear status message.
*/
public static void setErrorStatusline(final String message) {
Activator.getDefault().getWorkbench().getDisplay().asyncExec(new Runnable() {
public void run() {
View.getDefault().getViewSite().getActionBars()
.getStatusLineManager().setErrorMessage(message);
}
});
}
The RCP wizard generate code to log errors. Right now the program uses it to record its start up data and any fatal errors. Think of it as a job log. This method enables your classes to write to it.
/**
* Write message to log.
* @param severity
* IStatus.OK
* IStatus.INFO
* IStatus.WARNING
* IStatus.ERROR
* IStatus.CANCEL
* @param pluginId Identify plugin or class which wrote the log entry.
* @param message Message to log.
* @param exception Thowable if applicable otherwise null
*/
public static void logEntry(int severity, String pluginId, String message,
Throwable exception) {
Activator.getDefault().getLog().log((IStatus)new Status(severity,
pluginId, 0, message, exception));
}
The About action in the File menu will display the log, at least if anything has been written to it. Press the Configuration Details button. View Error Log button is on the bottom left, it will be grayed out if there are no log entries. The log, if present, will be openned by an OS application you choose, for Windows that would usually be Notepad.

ModelDemoOutputDqListener
Comment out System.out.println() output of the data queue entry read.
Add method to call View.addTableEntry with the message received from the data queue.
/**
* Add this message to the ArrayList which is displayed by the
* View and refresh the TableViewer.
* @param message String to be added to the table in View.
*/
private void addTableEntry(final String message) {
Activator.getDefault().getWorkbench().getDisplay().
asyncExec(new Runnable() {
public void run() {
View.getDefault().addTableEntry(message);
}
});
}
Comment out the line to set the status line with the message read from the data queue and replace it with call to addTableEntry(). The message read from the data queue will now appear in the view and not the status line.
// send output message to status line
// setStatusline( mdloMessage.trim());
// add output message to View
addTableEntry(mdloMessage.trim());
Define a constant to identify this class for the error log.
static final String classId =
"info.billblalock.eclipsercp.dtaqdemo2.dataqueue.ModelDemoOutputDqListener";
In the exception handler replace the stack trace with calls to Messages.logEntry() and setErrorStatusline(). The log will have the trace information and indicate what plugin or class wrote the log entry. The status line indicates to the user that a handled error occurred.
} catch (Exception e) {
// e.printStackTrace();
Messages.logEntry(IStatus.ERROR, classId,
"Error converting DataQueueEntry to model " +
"output dtaq record format", e );
Messages.setErrorStatusline(
"See log -- Error converting DataQueueEntry " +
"to model output dtaq record format");
}
Remove console output from ModelOutputDataQueue
The View class puts the data queue key as the first line of the table it displays. ModelOutputDataQueue no longer needs to write the data queue to the console.
Comment out
// System.out.println("Key used is: " + dqKey);
Remove the console output from the other classes in the dataqueue package. Have some fun and make your own output to the View, Statusline and/or Log.
Replace printStackTrace() with logEntry() and errorStatusline()
The ModelInputDataQueue.java and ModelOutputDataQueue.java classes report errors with printStackTrace() in one method. Modify the reportException() method of these two classes to write a log entry and set the error Statusline. Hint: This is a general reporting method, use e.getMessage() to get the description of the Exception for the log message and status line. Don't forget to add a constant to identify the class for the logEntry().
The method Activator uses both System.out.print() and Exception.printStackTrace(). This class can only write to the log because the UI is not established. In the start() method, where this error reporting is found, the UI has not yet been started. In the end() method the UI is probably closed. Replace this console output with Messages.logEntry().
Get creative.
You can clear the status line or error status line by calling the corresponding method in Messages with a null parameter.
You can write anything you want to the log. It is frequently used for debugging information. To separate your entries set the severity to OK, INFO or WARN.
Posted by Bill Blalock on May 13, 2007 at 11:00 PM | Comments (0)
This exercise builds an RCP application which monitor's the output data queue of the demonstration model. Eclipse RCP wizards are used to create an RCP application with a view that contains a table, the table shows a String[] with the values "One", "Two" and "Three." This blog entry shows how to run the wizards and modify the code generated by the wizards, and how to use the data queue classes created in Java data queues 211 blog entry.
The RCP application looks like when it first starts and determines the data queue key.

The console output of the key is a legacy of reusing classes from Java data queues 211. Future refinements will move this output to the user inferface and/or log file.
The is the RCP application after the CL view-controller on the System i puts the
- key "A80008",
- instruction "HELLO" and
- message "Bill Blalock"
in the demo model input data queue, the demo model has processed it and written to the output data queue.

Do you want to give it a try?
These are the pre-requisites
You worked through the RCP tutorial by Ed Burnette on the Eclipse web site or had some other basic introduction to Eclipse RCP. This blog tutorial exercise modifies an example RCP application produced by Eclipse wizards. The different parts of the RCP produced by the wizards are explained in Ed's tutorial. This blog tutorial tells you what to modify but not why unless it is specific to data queues.
You built the System i demonstration model and view-controller. The RCP works with the demonstration model. By itself the RCP doesn't do anything. The demonstration view-controller on the System i will be used to provide input to the demonstration model. Without that there won't be anything for the model to read and display.
If you don't have access to a System i but you want to work through the Java parts of these exercises please write me. If there is need I will see what I can do to get you access to the demonstration model and view-controller on the system which I time share on. No promises except that I will try if there are requests.
You built the console data queue examples in the previous set blog entries (Java data queues 101-211), particularly the last one (Java data queues 211). The classes developed in Example3d, that is 211, will be used in this RCP application.
Where is the java source?
Links to html of each class referenced are not provided. If you want to see the modified source use the CVS web access to this project, info.billblalock.eclipsercp.dtaqdemo2_301 .
Eclipse RCP wizards will generate the code to be modified. The changes are simple and documented in this entry. The RCP Eclipse tutorial explains the code generated by the wizards.
You already have the dataqueue code from the Java data queues 211 blog entry.
Step 1 -- Use wizards to create a skeleton RCP to build upon
New > Project > Plug-in Project
Give it a name, my project name is
info.billblalock.eclipsercp.dtaqdemo2
Press Next
Change radio button to Yes for "Would you like to create a rich client application?" Press Next
Make sure "Create a plug-in using one of the templates" is checked. Choose "RCP application with a view." Press Next. This creates a view with a JFace table, the table data provider is a String[] statically loaded with the values shown.
Change "Application widow title:" from "RCP Application" to "RCP DtaQ Demo" or whatever you like. Press Finish
You should now have a skeleton RCP application. Try it and make sure it runs. For now run it from within Eclipse so output to stdout with System.out.println is shown in the console at the bottom of the IDE.
Step 2 -- Add JT Open archive to your project
Create a folder within your project named "lib". To do this right click your project,
New > Folder.
Import jt400.jar into lib. Right click the lib folder,
Import > General > File System and press Next.
Find the folder with jt400.jar on your system, check jt400.jar and press Finish.
If PDE is not open double click MANIFEST.MF in the META-INF folder to open it. Choose Runtime tab at the bottom. Press Add to the right of the Classpath box. You will see a list of folders in the project. Expand lib and select jt400.jar. Press Finish.
In the Java and Plug-in Development perspectives you will see that jt400.jar now appears in the root of the project, not in the lib folder.
How to add the JT Open archive to a project was explained in more detail earlier in the blog. If you need a more complete explanation see the modified RCP Tutorial blog entries.
An optional touch is to add javadocs to jt400.jar so the javadocs will be available from the Java editor. Right click jt400.jar,
Properties > Javadoc location
and put in the folder with the JT Open javadocs.
Try the application to make sure nothing was broken. Fix any errors before proceeding to the next step.
Step 3 -- Add or enable standard Eclipse RCP features
3a. Modify ApplicationActionBarAdvisor class to add About top level action to File menu. About will provide access to the log file. See the "Branding" section in Part 3 of the Eclipse RCP Tutorial for more information about the About feature.
Make a copy of
private IWorkbenchAction exitAction;
and change the copied statement to
private IWorkbenchAction aboutAction;
Make a copy of
exitAction = ActionFactory.QUIT.create(window);
register(exitAction);
and change the copied statement to
aboutAction = ActionFactory.ABOUT.create(window);
register(aboutAction);
Make a copy of
fileMenu.add(exitAction);
and change the copied statement to
fileMenu.add(aboutAction);
Try the application to make sure nothing was broken. Fix any errors before proceeding to the next step.
3b. Modify ApplicationWorkbenchWindowAdvisor to show status line.
A standard RCP feature not mentioned in the tutorial is the status line, at the bottom of the main window. In this application the status line will show the message received from the demo model. Later it will also show the user errors and alerts.
Change the line of code
configurer.setShowStatusLine(false);
to
configurer.setShowStatusLine(true);
3c. Modify ApplicationWorkbenchAdvisor to save window size and location. Override the WorkbenchAdvisor.initialize() method by adding this method:
@Override
public void initialize(IWorkbenchConfigurer configurer) {
configurer.setSaveAndRestore(true);
}
Change the launch configuration (Main tab) to give you the choice to clear workspace data before launching and to ask for confirmation before clearing. Select both of these check buttons. Each time the application is launched is you will be asked whether to clear the workspace. Clearing the workspace returns the application to its initial size, location and space allocation.
3d Modify Perspective class to show an editor area for future use.
The editor area is to be displayed so comment out
layout.setEditorAreaVisible(false);
Change the line of code
layout.addStandaloneView(View.ID, false, IPageLayout.LEFT, 1.0f, editorArea);
to
layout.addStandaloneView(View.ID, false, IPageLayout.TOP, 0.50f, editorArea);
this puts the view on the top half of the window and saves the bottom half for the future editor.
Try the application to make sure nothing was broken. Fix any errors before proceeding to the next step.
3e View class needs a means for other classes to reference it.
Do this the same way that the Activator class provides a means for other classes to reference Activator through the getDefault() method.
Add a static View object
// The shared instance
private static View plugin;
Override the default constructor to initialize the View object which will be the reference
/**
* The constructor.
*/
public View() {
plugin = this;
}
Add a method to return the reference.
/**
* Returns the shared instance
*
* @return the shared instance
*/
public static View getDefault() {
return plugin;
}
Step 4 -- Create a dataqueue package and import classes created in DtaQExample3d
4a Create the package. The package is the same root name of the package which contains the source generated by the wizard plug ".dataqueue". For example the package created for my project is
info.billblalock.eclipsercp.dtaqdemo2
The dataqueue package I will create will be named
info.billblalock.eclipsercp.dtaqdemo2.dataqueue
Your package name is based on the project name you choose.
Right click the project,
New > Package.
Put in the name of the package.
4b Import classes from the Java data queue examples into the new package. My project name, and the name in the CVS is:
info.billblalock.mvcintro.java_apps
Import all the classes in the common package into the new package.
Import only these classes from the dataqueue package into the new package.
- DtaQExample3dListener.java
- ModelInputDataQueue.java
- ModelOutputDataQueue.java
Right click the package,
Import > File system press Next
Find the common package, select all the classes and import them
Right click the package,
Import > File system press Next
Find the dataqueue package, select the three classes named above and import them
4c Fix the errors now in the imported classes.
All the imported packages will indicate errors. The first step to fixing these errors is to change the package from
package common;
or
package dataqueue;
to (for my case)
package info.billblalock.eclipsercp.dtaqdemo2.dataqueue;
Use the package name you specified in step 4a.
The only error remaining should be in DtaQExample3dListener. Edit this class. The remaining error should be this import statement
import common.MDLOFMFormat;
Remove or comment it out. The common package doesn't exist in this project and the MDLOFMFormat class is now in the same package so it doesn't need an import statement.
The dataqueue package should now have no errors. If there still errors you are on your own to track them down and fix them. Some classes in this package will have to be tweaked to work with the RCP GUI instead of the console (stdout) but most of the work is done and tested. How about that, reuse of classes!
Try the application to make sure nothing was broken. If there are still errors in the new dataqueue package you will get a warning that errors exist in the project. Since these classes aren't referenced yet the project will still run so press Proceed. Fix any errors before proceeding to the next step.
4d Use DtaQExample3dListener as the property change listener for this project.
Refactor > rename
DtaQExample3dListener.java
to
ModelDemoOutputDqListener.java
4e Add a method to the renamed listener class to update the status line of the RCP window. This is one means the listener will communicate what happens when a model output data queue entry is read, or attempted to be read.
/**
* Set status line of RCP with this message.
* @param message String to be placed on status line.
*/
private void setStatusline(final String message) {
Activator.getDefault().getWorkbench().getDisplay().syncExec(new Runnable() {
public void run() {
View.getDefault().getViewSite().getActionBars()
.getStatusLineManager().setMessage(message);
}
});
}
The wizard created the Activator.getDefault() method to allow other classes to get an instance of the running Activator.. You added View.getDefault() to the View class created by the wizard in step 3e to do the same thing.
The listener is running of in its own thread, totally disconnected from the thread in which the RCP is running. This is going to grab a reference to the thread in with the RCP UI is running and call the UI method to write the status line.
4f The listener now writes the data received from the data queue to the console. Also send the message received from the data queue to the status line.
Find this code:
// Get two values out of the record and display them.
String mdloInstruction = (String) mdloData.getField("OINSTRTN");
String mdloMessage = (String) mdloData.getField("OMESSAGE");
System.out.println(" * The model received instruction: "
+ mdloInstruction.trim()
+ "\n * and returned data: "
+ mdloMessage.trim());
System.out.println(" *********************************************");
Then add this line of code:
setStatusline( mdloMessage.trim());
This class uses the method just added (4e) to send the message to the status line.
Try the application to make sure nothing was broken. You will get a warning that errors exist in the project. Since these classes aren't referenced yet the project will still run so press Proceed. Fix any errors before proceeding to the next step.
The next step makes RCP DtaQ Demo a partner with your System i so it needs to be working at this point. This is the last chance you have to test the RCP without needing to be connected to a System i and have the demonstration model running.
Step 5 -- Connect RCP DtaQ Demo to the AS400 through the Activator class.
Now the project is ready to connect to the AS400.
The pseudo code logic of Java data queues 201 Example3d, the main() method, is:
i. set up the System i connection
ii. instantiated the Runnable, attached the listener, created the Thread that listens to the model output data queue and started the Thread.
iii. instantiated the object to write to the model input data queue
iv. looped accepting input to send to the model input data queue
v. looked for the end signal, *STOPITNOW, to break the loop
vi. closed down the Thread and disconnected from the System i.
Activator will be modified to do steps i, ii, iii and vi. The UI nature of the RCP replaces the need to loop waiting for input (iv). The exit action in the File menu or upper right X takes the place of the end signal (v).
5a. Add these class global variables to Activator
private static AS400 as400;
private static Thread mdloDqThread;
private static ModelOutputDataQueue mdloDq;
5b Instantiate the AS400 object in the constructor
as400 = new AS400();
5c Add this code from DtaQExample3d (see previous blog entries) to the start() method
after the call to the base start() method:
super.start(context); // base start() method// added code
try {
as400.connectService(AS400.CENTRAL);
// create instance of Runnable for Thread which reads data queue
mdloDq = new ModelOutputDataQueue(as400, Me.getMDLO_DTAQ(),
IpDtaQKey.getDqKey() );
// execute the run() method of the thread
mdloDqThread = new Thread(mdloDq);
// register a separate class as the listener for
// property change events
mdloDq.addPropertyChangeListener(new ModelDemoOutputDqListener() );
// execute the run() method of the thread
mdloDqThread.start();
if ( !as400.isConnected() ) {
System.out.println("... unable to connect to System i");
System.exit(0);
}
} catch(AS400SecurityException se) {
System.out.println("... signon cancelled");
System.exit(0);
} catch(Exception e) {
e.printStackTrace();
throw e;
}
5d. Modify the stop() method to shut down the Thread which reads the model's output data queue. After the call to the base stopt() method add this code:
super.stop(context); // base stop method// added code
mdloDq.stopReadingDq();
try {
mdloDqThread.join(5000);
} catch (InterruptedException e) {
// do nothing
}
as400.disconnectAllServices();
as400 = null;
The stop() method now implements part vi of Example 3d. The stop() method is called when the UI shuts down.
Interlude --
At this point RCP DtaQ Demo can read the demonstration model's output data queue and display the message in the status line. You have to run it within Eclispe so that you can see the console output (System.out.println … we haven't eliminated it yet). This is a good place to take a break.
Here is how to test it.
1. Start the demonstration model on the System i. Use the demonstration CL view-controller on the System i to make sure it is running properly.
2. Start RCP DtaQ Demo in Eclipse so the stdout output goes to the console window.
3. You will be prompted for the AS400 connection information. After the connection is established the key that is generated from the IP address is displayed in the console.
4. Note the key generated -- in my case it is A80008. See the image at the introduction of this entry.
5. Use the demonstration view-client CL program on the System i to cause the model to write a data queue entry to the model's output data queue.

Use the key that the RCP is listening for. Use the CL option for the view-controller to only write the model output data queue (last parameter is *YES), we want RCP Dtaq Demo to read it … not to have the CL view-controller read it first.
6. Shortly after the Enter is pressed the message from the demonstration model will show up in the status line and the console will show the stdout output. showing message in status line and console.jpg

The source is in the CVS. The project is info.billblalock.eclipsercp.dtaqdemo2_301. I renamed the project so that this version would remain as it is, at version _301, and futher development would be posted under other versions.
RCP DtaQ Demo will later be enhanced to
- remove the console output, use the UI and log errors
- display the data queue messages in the view
- accept demonstration model input from the user.
I'll try to go a little more slowly, make the entries smaller and have more explanation since the new materials aren't covered by the RCP Tutorial (Ed Burnette) and Java data queues 101-211. This one turned out rather long because this is what it took to go from nothing to an RCP application which did something.
Posted by Bill Blalock on May 6, 2007 at 7:00 PM | Comments (0)
Now we have a
- demonstration model for MVC running on the System i written in RPGLE
- demonstration view-controller for MVC on the System in written in CL
- demonstration console based view-controllers for MVC written in Java which locks the application while waiting to read the output of the model (DtaQExample1 and 2)
- demonstration console based view-controllers for MVC written in Java where a Thread waits to read the output of the model (DtaQExample 3a, 3b and 3c).
What about Eclipse and Rich Client Platform? Remember, the subject of this blog. Actually we are closer than you might think. In this blog entry DtaQExample3a and DtaQExample3Thread will be refactored into parts which can be re-used. We will take those parts to Eclipse RCP and continue their development.
Refactor DtaQExample3Thread into a generalized, generic form
The new class is ModelOutputDataQueue. This class will handle the data queue output of a Model (MVC), any Model of which follows the design presented. In order to do this anything which ties the model's output data queue handling to the specific Demonstration Model has to be moved out of this class. This class will work with the demonstration Model running on the System i and future Models, given some help outside the new generalized class.
In fact this is a very generalized keyed data queue reading class which can be run in a Thread. Considered in isolation, it is independent of the whole notion of MVC. It could be used anywhere that a keyed data queue needs to be read from a Thread.
Many of the design aspects for DtaQExample3Thread are retained. The changes are:
1. The new class doesn't assume anything about the data being read from the data queue. Reading the keyed data queue returns a KeyedDataQueueEntry, a generic object which assumes nothing about the data or its format. Taking the data from the KeyedDataQueueEntry and putting it into a Record is done outside the new class (by the Listener). A generalized, reusable class won't know what the data which the model will send looks like, the data structures may not have even been designed.
2. Give the class a way to stop the run() method short of crashing the data queue. This is done by the method stopReadingDq(). This method sets a flag which breaks the run loop then writes a dummy entry to the data queue which the run() loop is reading. The run() loop reads the dummy entry, sees the flag is set and breaks out without firing the PropertyChangeEvent.
A side note: A refinement would be for the PropertyChangeEvent to fire a "dqEnded" event before breaking the loop. That way the PropertyChangeListener could act on the fact that the program is no longer reading the Model's output data queue.
3. Every 10 minutes the read() times out then re-read the data queue. This gives the program a chance to throw an error if the data queue isn't available.
4. Create Exception stubs for everything the System i could throw. Later fill in as needed.
5. Consolidate exception reporting into one method. Right now this method simply does a printStackTrace(). In future implementation this method will be expanded to log errors and to take some action like cause the program to terminate. Remember the run() method of a Thread can't throw an exception. The reporting method will send a dqError PropertyChangeEvent to the PropertyChangeListener so it can notify the rest of the program something happened.
6. The class implements Runnable instead of extending Thread. The user of this class will create its own Thread, instantiating it with this class.
Refactor DtaQExample3a to put the data queue writing logic into a separate class
The new class is ModelInputDataQueue. This class separates the function of writing to the Model's Input Data Queue from the main application DtaQExample3d. The main application performs the functions from a specific implementation of a Model, such as the Demonstration Model running on the System i. This class only writes the data queue as DtaQExample3a did, but without any model specific details. The changes to generalize the class were done for the same reason as the Output class:
1. The new class doesn't assume anything about the Record which will be written. Instead the Record will be passed to the write() method.
2. Create Exception stubs for everything the System i could throw. Later fill in as needed. . The write() method can throw exceptions so the caller of the write() method will have to be prepared to handle (or ignore) them.
3. Consolidate exception reporting into one method. Right now this method simply does a printStackTrace(). In future implementation this method will be expanded to log errors and to take some action like cause the program to terminate.
DtaQExample3aListener becomes DtaQExample3dListener
DtaQExample3dListener required only one change. DtaQExample3aListener received a Record from the Event fired by the Thread. Record is specific to a RecordFormat which is specific to a System i file. DtaQExample3dListener receives a KeyedDataQueueEntry which is generic. The class builds a model output Record from the KeyedDataQueueEntry sent in the Event.
Only a few lines of code are changed to accomplish this. The Listener becomes very specific for the data it listens for while the Thread that is being listened to knows nothing about the data. It just reads a KeyedDataQueueEntry and passes it on to the Listener.
The PropertyChangeListener which will be attached to ModelOutputDataQueue can't be standardized into a generic class because what is does is specific to
- the Model being implemented,
- the data sent to the listener in the PropertyChangeEvent object, and
- possibly the name of the event fired.
DtaQExample3a becomes DtaQExample3d
DtaQExample3d is the main application. The ModelInputDataQueue class now has no dependencies on the specific Model that it is used with. This means that their caller (DtaQExample3d) must creates the model input Record required for the implemented Model and pass it to the write() method.
The code to write the model input data queue is removed from the main application (DtaQExample3a) and replaced by calling the write(Record) method of ModelInputDataQueue.
A Thread is created to run ModelOutputDataQueue.run() in place of DtaQExample3Thread.
To shut down the program call ModelOutputDataQueue.stopReadingDq() to end the Thread. After calling this method wait for the thread to end with Thread.join(milliseconds). After the Thread ends or the join times out close the connections to the System i and end the program with System.exit(0).
Replace Me.getKEY() with the class IpDtaQKey
IpDtaQKey generates a key based on IP address. The key represents a View-Controller which communicates with the Model. Often the VC will be running on multiple desktops. IpDtaQKey creates a key for the model data queues from the IP address of the desktop client. This will be unique per desktop.
Another side note: One caveat though! This breaks down if two or more clients were to run separately on the same desktop using the same set of data queues. In this case the key would have to be "uniquified" for each client (on the same desktop using the same data queues).
Java is not well suited for this. Each JVM lives in its own world and is unaware of any others which may be running on the same system. The JVM can't make use of the operating system resources for coordination because the JVM is intentionally blind to operating system specifics.
To solve this I'd use this approach.
1. Decide on a folder any JVM on the desktop can find. Make sure it is not dependent on the user logged on, that is not dependent on the users home directory. For example C:\TEMP.
2. Create a Collection object of "unique" values of 0 to 255 which can be represented by x00 to xFF. The unique value will be the look up if you choose a Map type Collection. If you choose an Array type collection than the unique value will be the index. I'd also put a timestamp on it so old value could be recycled if they hadn't been removed.
3. Each time the program which needs a key starts up it
- calls the IpDtaQKey for the base key,
- deserializes the Collection,
- finds a unique value and convert it to hex (2 characters),
- updates the Collection with the unique value used,
- cleans up any obsolete used unique values (from the timestamp),
- then serializes the Collection.
The data queue key for this instance is IpDtaQKey.getDqKey() + uniqueValue
4. When the program ends deserailze the Collection, release the unique value so it can be re-used and serialize the Collection. The clean up in the previous step is a back up because programs crash and you can't depend upon this step being executed each time a new unique value is put in the Collection.
Like I said, Java doesn't make it easy. Please let me know if you have better ideas. This is an on-going problem for me. What I have described is the best solution I have come up with to date.
If you run these examples in QSHELL on the System i then you need to do something else for the key. I have started a dummy job, gotten its number, ended the dummy job and used the dummy job's job number for the key.
Try to avoid a design which requires the key value to be created by the user in a property or environment variable. This works great during development but when you start distributing the application you will find that maintaining a unique key per installation is a real pain. Been there, done that. Bad!
Play with running both the CL VC and DtaQExample3d!
Actually you can do this with any of the DtaQExample programs. The CL demo View-Controller can write an entry to the demo Model output data queue without reading it back. If you do this while DataQExample3# is running, with the same key the instance of DtaQExample3# is using, the instruction and data will pop up on the console.
All the entries on the demo model data queue are being placed on it by the demo Model running in batch on the iSeries. The difference is where the input to the Model came from, the System i CL program on the desktop Java application. DtaQExample3# shows both and doesn't treat them any differently.
The thread to read the KeyedDataQueue starts to show its power when messages pop up on the console from the model while the console is reading stdin for your input.
The code is in the CVS
The new classes have been added to the dataqueue package except for IpDtaQKey which was added to the common package.
Taking some time off
The next blog entry set will be about writing an Eclipse RCP demonstration View-Controller that has been implemented as
- a CL program
- as a Java console program (6 different ways now)
I am experimenting with moving my Eclipse 3.2 work into WDSc 7. If these experiments are successful then the next exercise will be done in WDSc 7 and include notes on how to do it in Eclipse 3.2.
Right now my personal preference is native Eclipse. However WDSc 7 has advanced to the point that managing two separate development environments may outweigh the extra capabilities of native Eclipse. I will see and get back to you on this.
I am taking some time of posting this blog to take care of this and other things.
In the mean time please write if you have questions or comments. Please write to tell me what you'd like to see in an Eclipse RCP application version of this Java console program. Tell me what parts you would like explored in detail.
Thanks all. Enjoy the spring -- that is part of my "other things" :)
Posted by Bill Blalock on April 29, 2007 at 6:00 PM | Comments (1)
A previous blog entry "Java data queues 201 -- the thread side of a thread based console application view-controller for System i demonstration model" solved the problem of the java application being stuck waiting to read a data queue by putting the read data queue part of the program into a separate thread. Each time a data queue read occurs the thread fires a PropertyChangeEvent. The event contains the information read from the data queue.
This blog entry address the rest of the example application. A large part of the main() portion of this application, DtaQExample3#, is the same as Examples 1 and 2 in that they
- get input from the console and
- write it to the model input data queue
(remember that demo model running on the iSeries?). See the early discussion.
The different (new) parts of DtaQExample3# is that
- the model input data queue (read) is started as a thread (DtaQExample3Thread)
- PropertyChangeListener is implemented
Three examples (3a, 3b and 3c) show different ways to implement PropertyChangeListener so you can experiment with the differences.
DtaQExample3a implements PropertyChangeListener as a separate class.
DtaQExample3b implements PropertyChangeListener as an anonymous inner class.
DtaQExample3c implements PropertyChangeListener in its declaration and provides the methods of PropertyChangeListener
The PropertyChangeListener (PCL) in these examples listens for the PropertyChangeEvent (PCE) named "readDq". When it receives on it creates Strings from the data (Record) contained in the PCE and displays the Strings on the console.
If you look at the propertyChange() method of PCL in all the three examples you will see they are identical.
Example3a implements PCL as a separate class, DtaQExample3aListener. This isolates what is going on the in PCL from the rest of the application. You choose what to provide this class in the constructor design and the setter methods, and choose what to expose with the getter methods.
Example3b implements PCL as an anonymous inner class. An inner class is a separate class that has limited assess to data in the class which creates it and does not exist outside that class.
Example3c implements PCL in the class declaration, at the very top of the program. Because the Example3c class formally implements the PropertyChangeListener class it must provide the required method
public void propertyChange( PropertyChangeEvent event )
The method has full access to the class variables of Example3c and easily interacts with the rest of the class.
The choice of how to implement PropertyChangeListener depends on the design.
The advantages of a separate PCL class (3a) are
- it can be re-used
- it puts the PCL and what it does into a black box
The downside is more design work and coding is required.
The advantage of an inner class to implement PCL (3b) is that it is quick to write and can interact with the data in the parent class. It is a good choice if the inner class does very little and won't be reused.
The downside, at least for me, is that inner classes can be messy, hard to read and have a tendency to grow as the design changes.
The advantage of implementing PCL at the class level (3c) is that the work of the listener is put into the propertyChange() method and that method can easily communicate with the rest of the class.
I really don't see a downside when this approach fits with the design. That is the PCL is used only by, and communicates with, the one class which implements it.
Posted by Bill Blalock on April 21, 2007 at 1:00 PM | Comments (0)
The prior blog entry presented a Java console application which performs the same function as the CL view-controller -- write a data queue then read. The examples used the techniques covered in IBM's documentation. That was a starting place -- putting the classes explained in the Java toolbox documentation into a console view-controller.
The write-then-read pattern does not work for GUI clients. The desktop application has to be free to service inputs from the user while waiting for the data queue read to happen. The program can't be locked waiting for model output on the data queue.
The Java console application of the last entry is re-written to place the data queue read portion of the write then read pattern in a separately running thread. The main program spawns a thread which waits for the model output data queue and reads it. After the thread is spawned the main program is free to do whatever … in this case wait for input from the user and write to the model input data queue.
I haven't found examples of using JTOpen Java toolbox data queue classes in threads. Over the years I have developed several techniques of running data queue classes in threads.
dataqueues.DtaQExample3Thread.java is a class that reads the model (or server) output data queue. When a data queue entry is read the class passes the Record object read to be processed. In this case the Record object, that represents the model output data structure, is turns it into Java variables (String objects) and written to the console.
The operation of this class is narrowly defined:
- wait for data on the model output data queue
- when the data is read package it into a Record object
- fire a PropertyChange event which contains the Record object which was just read and wait for the next data queue entry
- handle any exceptions
- when told to clean up and end the thread
My experience is that the thread which reads a data queue should do as little as possible but do it well. Most of its time is spent waiting on a data queue.
Important points in this class are
a) the AS400 object is instantiated and passed to the thread object when it is created by the constructor
b) the class handles its data queue exceptions
c) the class cleans up after itself and shuts the thread down (thus shutting itself down)
d) the java bean PropertyChange classes are used by the thread to communicate
a) The AS400 object is a shared resource, shared by all the parts of the Java application running in one instance of the JVM. It is created and managed outside the thread. The thread merely uses it and handles any exceptions which result from the threads use of the AS400 object. That is why it is passed to the thread in the constructor.
The AS400 object will used for other services between the Java client and the System i server. Reading the model output data queue is just one use. For this example the AS400 object will also be used to write the input received through the console to the model's input data queues. In a real MVC scenario the AS400 object would also be used by message queues and perhaps JDBC or record access services.
b) The thread waits to read the data queue. What if something goes wrong? The thread has to handle the error or at least pass it back to the program. My experience is that it is best if the thread handles the errors. Most of the errors are extremely fatal. To keep this simple I just dump the stack trace and crash (System.exit(0)).
c) The thread spends most of its time waiting for a data queue entry. It can't do anything else while it is waiting. How do you tell the thread to stop waiting and do something else?
In this example I took the cheap way. One thing that the thread will notice while it is waiting for a data queue read is if the data queue goes away! The thread specifically traps for the condition that the data queue went away. When that happens the thread cleans up and terminates its self. The caller ends the data queue services, gives the thread a few moments to notice and end itself, then goes about its business.
I have used two other methods, both more sophisticated than pulling the rug out from under the data queue thread.
1) Send the thread a message to shut down through the data queue it is reading. Remember, the thread is reading a data queue. It doesn't know where the message originated. If the thread processes a "terminate" message then either the main Java application or the System i server can send this message. In the case of a Java rich client the main Java application would send the message to "terminate" during the shut down process. In a more traditional client server program the server might send the terminate message and the thread pass the information to the main program, perhaps shutting it down.
2) Instead of waiting forever for the data queue read waits a short period of time. When the read times out the program looks to see if it should continue reading the data queue. I use this in conjunction with a shutdown() method in the thread.
- The main program call DtaQThread.shutdown().
- shutdown() sets a shutdown flag in the thread
- the next time the read times out the program sees that shutdown flag is set and acts. Otherwise it goes into another read cycle.
Waiting forever on a data queue read has subtle problems for long running client server programs. Basically the code reading the data queue will never get data, or see that there is a problem, if the data queue goes away. Seriously, the JTOpen toolkit read data queue class won't notice that the System i has been IPLed if the data queue read timeout is a -1 (forever). If the data queue read times out and then read again for N seconds it will see that something happened to the data queue (like it is no longer there or was recreated) and can handle the error.
For simplicity sake in this example I took the easy way out. When the main program is stopped by the user typing *STOPITNOW the data queue service is disconnected. The thread will take that error as a signal to clean up and die.
d) Data is read from the model output data queue. Now what? How is that communicated to the main program? For this example I chose java been PropertyChange classes but there are several ways it could have been done.
I don't use the DataQueueListeners which can be attached to a DataQueue object because they don't work well to my mind. If you want to discuss it leave a comment.
The Java bean PropertyChange classes are a good choice. The thread can read the data, do whatever processing it needs to do, and fire a PropertyChangeEvent that contains the data read from the data queue. The code controls the point the change event is fired and what is communicated to the listeners.
The PropertyChangeListener "hears" the fired event and acts on the data. In this case the PropertyChangeListener unravels the Record object from the data queue read and writes it to the console.
There are several other ways to do this. I have tried a few of them. Please leave a comment if you have another way to do it.
Also note that a GUI client is a multi-threaded program by nature and may already provide a means for the thread to notify the rest of the client that a data queue entry has been read. Remember, the purpose of this example is to give an alternative example of data queue use for a rich client. This is just a starting point, something simple to explore and experiment with.
Enough for now -- perhaps too much, certainly not enough.
The other parts of this example, Example3, will be addressed in the next blog entry. There are three version of Example3, all use the same DtaQExample3Thread class. Here is the code for the rest of the example if you want to try DtaQExample3Thread.
DtaQExample3a implements PropertyChangeListener as a separate class.
DtaQExample3b implements PropertyChangeListener as an anonymous inner class.
DtaQExample3c implements PropertyChangeListener in its declaration and provides the methods of PropertyChangeListener
Enjoy!
Posted by Bill Blalock on April 8, 2007 at 8:00 PM | Comments (0)
This set of blog entries focuses on using data queues in Java. Data queues, and later message queues, are the foundation for the model <> view-controller interchange between a System i and a Java application (Eclipse RCP or otherwise) developed in this blog series.
The last blog entry implemented an RPG System i demonstration model using data queues. The demonstration model reads an input data queue and writes to an output data queue. As a bonus the entry provided a CL view-controller to test the model. Now focus goes to the Java side, putting data on the input data queue for the model and reading the response from the model on the output data queue.
This blog entry presents two versions of a Java console application which performs the same function as the CL view-controller. The examples use the techniques covered in IBM's documentation. I look at this as a starting place -- putting the classes explained in the Java toolbox documentation into a console view-controller application.
Consider this a review of data queue related classes and examples provided by IBM. That documentation is found in the iSeries Information Center, under Programming > Java > Toolbox section. You can compare the Java code to the data queue examples which IBM provides.
In the last blog entry the CL view-controller gave you a means to exercise the demonstration model which was running in batch with a couple of options (write to the model but don't read and read from the model but don't write). This blog entry provides the same basic tool in Java. You can run this on the System i (in QSH) or on your desktop.
The code for this is in the CVSDude repository, in the project "info.billblalock.mvcintro.java_apps". The programs are
- dataqueue.DtaQExample1.java and
- dataqueue.DtaQExample2.java.
The programs are similar with one significant difference I will get to later.
The logic of these programs is like the CL view-controller in that they write an entry to the demonstration model's input data queue and wait to read an entry from the demonstration model's output data queue. This pattern is similar to EXFMT of a 5250 display, write the display file and wait for a read.
The console prompts the user for the instruction to send to the model. If the instruction is *STOPNOW the program terminates. If not the program prompts the user for the data to send with the instruction. The instruction and data is written to the model input data queue. The program waits to read the model output data queue.
The class common.Me.java defines constants and getters for your AS400, data queue names, etc. That is the only thing you should need to change to get these programs to run on your system.
These two Java client programs will run either on the System i as Java programs or on your desktop. There is no GUI involved, all the IO is through stdin and stdout.
The only difference between Example1 and Example2 is that Example1 dynamically builds the Record object and Example2 uses a static pre-compiled Record object.
Example1 doesn't know anything about the data structures for the input and output data queues that are expressed as System i files (external data structures). It dynamically retrieves the files from the System i and learns the structure of the data from the files. Each time the program is run the data structures are resolved.
Example2 uses precompiled classes to represent the data structures. It doesn't have to retrieve this information from the System i.
Example1 shows the time it takes to retrieve the information during the startup of the program. Example2 shows that virtually no time (0 milliseconds) is required to use the precompiled information.
I did this comparison because while the Java toolbox documentation mentions the two methods it doesn't give you a good feel for the real difference.
Example1, the dynamic example, limits the impact of changes in data structures. Dynamic resolution means your program won't care if new fields are added to the data structures, if lengths of fields are changed or if the order of field names is changed. But you pay for this flexibility each time the Java program dynamically fetches the information. Of course the program is still going to tank if the names of the fields or their data types are changed.
Example 2 is fast. If the external files on the System i which represent the external data structures are changed then the pre-compiled classes will have to be re-generated.
That is pretty much gives you all you need to know to decide between the two methods for your given circumstances.
The class common.CreateModelRecordFormats.java is the code to generate the static, or pre-comiled classes to represent the data structures based on System i files. See the toolbox documentation for details.
I am writing less blog and putting the time into the code. If I missed something or there are questions leave a comment.
Enjoy!
Posted by Bill Blalock on April 1, 2007 at 6:00 PM | Comments (0)

| Sun | Mon | Tue | Wed | Thu | Fri | Sat |
|---|---|---|---|---|---|---|
| 1 | 2 | |||||
| 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 10 | 11 | 12 | 13 | 14 | 15 | 16 |
| 17 | 18 | 19 | 20 | 21 | 22 | 23 |
| 24 | 25 | 26 | 27 | 28 | 29 | 30 |
| 31 |
Our blogs are editorial content of System iNetwork. We welcome your comments and opinions and encourage lively debate on the issues, and we reserve the right to edit all postings for clarity, length, civility of tone, and appropriateness to the topic under discussion. Comments consisting of product or job solicitations and other spam, profanity, and extreme rudeness will be deleted. We also reserve the right to publish excerpts from the blogs in our e-mail newsletters and print magazine.