29
Fri, Nov
0 New Articles

Grids: Subfiles for Java Programmers

Java
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

For interactive application programming on the AS/400, nothing beats a subfile. Subfiles are so important to the AS/400 that the first question asked of job applicants who boast of their programming strengths is “Can you code subfiles?”

Subfiles allow interactive application programmers to easily present multiple and related records on a 5250 terminal. Subfiles present a visual subset of a database file that can be written to, updated, and retrieved. Subfile records can contain the complete spectrum of AS/400 data types with sophisticated built-in editing capabilities.

So what will the next generation of AS/400 programmers do with their Java applications to give users similar functionality? Perhaps you have already read one of the hundreds of Java books available and are wondering “Just what is going to replace my subfiles?” Java 1.1’s standard components fall far short of providing anything that will replace a subfile. The Abstract Windowing Toolkit (AWT) supplies a list box, but that only presents a simple list of unformatted strings, with no dynamic entry facilities and no support for various data types.

I have programmed more subfiles than I can remember. But I have also programmed Java graphical applications using a medium that is very similar to a subfile—the grid. A grid is like a spreadsheet in that it is a two-dimensional table of rows and columns. A grid, like a subfile, presents a set of related records in a tabular format. But a grid also provides significant value add over subfiles. A grid supports undo and redo, its rows can be dynamically sorted, and each of its cells can contain visual editors like checkboxes, choice lists, and date calendars. And, because a grid is graphical, it can support images and window resizing and respond to the typical windows events like keystrokes and mouse clicks. This article presents grids as the replacement paradigm for subfiles.

Grids Versus Subfiles

Grids are powerful software components. C and C++ programmers have been using them for years to present multiple rows of database records and multiple columns of

database fields of various data types. Software components are reusable pieces of code that can be easily assembled to create applications. Using software components for development is much more efficient than developing the software yourself. A grid component can be placed on a window panel. Each cell within that grid itself contains a software component. Usually, all the elements for a particular grid column are assigned the same component. For instance, the last column in Figure 1 has a checkbox component assigned to it to allow the intuitive activation or deactivation of a customer’s status. The component assigned to the other columns is a simple text string editor. The rows of a grid typically contain database records except for the first row, which is used for column headers. The header row normally contains simple labels, but often they are set as buttons to enable features such as a dynamic sort by a user selected column.

There are obvious similarities between grids and subfiles. You still have a subset of a database presented in tabular format, and you still can browse through multiple pages of those records with scroll keys. Grids are also enhanced with windows scroll bars. The scroll bar’s thumb button’s position gives the viewed record’s relative position in the grid. The height of the thumb also gives some representation of the amount of records in the grid. But try to find something comparable to subfile control record formats or subfile record formats in Java grid classes and the analogy appears to breakdown. What we are dealing with is a paradigm shift from subfiles to grids. This shift seems overwhelming, but keep in mind what it was like when you first began to learn DDS. Also, keep in mind that, by learning and understanding grids, you will acquire the full benefits of the broad capabilities of both grids and object-oriented programming.

Grid Choices

A perplexing problem with grids is that there are so many of them to choose from. With subfiles, as with most everything in your AS/400 programming arsenal, you had one choice—IBM. However, that’s not the case with grids; you have choices. I have used grids from four different vendors: Stingray, Rogue Wave, Microsoft, and SunSoft. Another grid worth mentioning is Taligent’s MultiColumnListbox because it is used in the Java examples found in IBM’s Redbook Accessing the AS/400 System with Java. Taligent’s grid is extremely simple to use, but it does not provide the breadth of capabilities that other grid vendors provide. Both Stingray and Rogue Wave deliver industrial-strength grids—but at a price. SunSoft’s grid, JTable, as a standard part of the base Java 1.2 Java Virtual Machine (JVM), is free. But then, Microsoft’s grid component is also free.

Microsoft’s AFC Versus SunSoft’s JFC

SunSoft’s JTable grid is one of the main components of its Java Foundation Classes (JFC), just as Microsoft’s grid is a part of its Application Foundation Classes (AFC). You may already be following the feud between Microsoft and SunSoft, a recent skirmish of which is over JFC and AFC. SunSoft decided to make JFC a standard part of Java 1.2, so JFC classes need not be dynamically downloaded for Web applications that use JFC components—that is, if the Web browser running the applet is SunSoft- compliant. Microsoft’s browser, Internet Explorer (IE), does not and will not include JFC. Rather, it will include AFC. Microsoft says it has no intention of supporting SunSoft’s JFC because the Microsoft technology is better. Microsoft also does not support standard Java’s Remote Method Invocation (RMI) or Java Native Interface (JNI)—both of which are vital to AS/400 Java Web or client/server applications.

You can get Microsoft’s AFC-based applets to work with Netscape, but it is not simple or straightforward. On the other hand, SunSoft’s JFC will work in IE, although the classes will have to be dynamically downloaded as they are not in the IE JVM. If your Java applications are to be limited to platforms that are based on Microsoft’s operating

system, AFC is probably a reasonable choice. However, I suggest that you start application development with JTable and review third-party grids for features that may be important for your application.

The JTable Grid

I developed the example Java grid application displayed in Figure 1 using JFC’s JTable. JTable, like all the JFC components, conforms to the JavaBean specification, which is now the industry standard interface for Java components (although Microsoft might not agree). Once you use and learn one component written to the JavaBean standard, it is easier to pick up and use other components that also follow this standard. JFC use should be widespread as it is powerful, well-designed, easily extensible, and free. IBM is also on the JFC design team, so rest assured that the AS/400 should be well-represented.

Implementing an application that employs a JTable grid requires the use of a group of Java classes, only one of which is named JTable. This makes it rather confusing when you first begin to use JFC’s JTable grid. But JTable and its supporting classes were designed so that each class encapsulates functionality specific to one conceptual area. These JTable classes were implemented with an architecture known as Document/View. This architecture separates the code for data manipulation (the document) from the code for display manipulation (the view) into different classes. The JTable class called JTable handles the graphical presentation or the View. The document class is your application’s implementation of the associated JTable class—AbstractDataModel. In my example application, the class derivation of AbstractDataModel is called CustMastDataModel.

If you look at the top of the code in Figure 2, you can see the CustMastGrid application driver class, the section of code labeled A. It is the public main() function that is invoked when you specify this driver class as a parameter when calling the JAVA.EXE program:

JAVA.EXE CustMastGrid yourAS400sDomainOrIPAddress

CustMastGrid’s main() then bootstraps the application by invoking Java’s new operator on the CustMastGrid() constructor function. Note that main() takes a string parameter that should be a valid IP address or domain name of an AS/400. CustMastGrid() in turn, as identified by Label B, creates a JFC frame. Later on, CustMastGrid()’s JFrame will have a JTable grid inserted into that window frame. As my standard, I have copied the code to handle the window closing event with an inner class that simply performs the system exit function.

The CustMastGrid class constructor function (which, like all constructors, bears the same name as its class) creates a model of the data to be presented. Remember that a JTable grid follows the document/view architecture. Well, the CustMastDataModel class instantiated at Label C creates the data model. The code in Label D then creates the view object (the JTable) and associates to it the document (the instance variable dataModel) by passing a reference to the document object to JTable’s constructor. Notice that I am using a custom data model class, the CustMastDataModel, but I am using the default JTable class. The JTable class visually presents a two-dimensional table but delegates the acquisition and processing of the data displayed within the grid to the data model.

The Document

To clarify this association between the dataModel instance variable and the JTable, I am going to jump ahead to the CustMastDataModel’s class code implementation. CustMastDataModel’s class fields are simply an array of header strings and a vector of records (a vector is a dynamically sizable array). The CustMastDataModel() constructor

The View

function is invoked with the Java new operator at Label C. The CustMastDataModel() constructor starts out in Label H by connecting to the AS/400 specified with the passed IP address string. The constructor function then creates an object to contain a path to a fully qualified AS/400 file name—the Customer Master file in my library. That path is then used to create an object to handle sequential file processing. (Remember that this is object- oriented programming.)

Notice that the path object (QSYSObjectPathName) and file object (SequentialFile) are each instantiated with a call to Java’s new operator followed by the class name and the parameters required by the class’s constructor function. (For keyed processing, you would have instantiated a KeyFile class.)

Record Format

By the time execution reaches Label I, I have created a connection to an AS/400 and have specified an object to handle sequential processing for the Customer Master data file. The code at Label I is designed to retrieve the Customer Master’s record description dynamically. Note that I can optionally statically define the record description to improve processing speed, but CustMastDataModel would lose the ability to dynamically handle AS/400 record format changes (without a recompile).

CustMastDataModel then attempts to set a record format from the retrieved record description. I say “attempts” because the retrieval of the record format is wrapped around a try block. A try block is similar to a Monitor Message (MONMSG) statement in a CL program, and its associated catch block is like the EXEC portion of that MONMSG statement. If something goes awry within the try block’s code, the catch block’s code will execute. If a class’s function generates, or “throws,” an exception, Java forces you to monitor for that error with a try block.

The next step is to set the record format of the Customer Master file object to the retrieved record format. This setRecordFormat() function must also be enclosed within a try block, but I must admit that my error-handling code within the catch block leaves much to be desired.

Populate Grid

Now that the file processor object has a record format set, I can finally open the Customer Master file and populate the data model’s internal storage of records in the records vector. It does not populate actual JTable grid, as you might expect. Remember that the JTable is simply a view. The actual data buffer resides in the data model—the CustMastDataModel. The records vector is filled with the for loop located a little below the code at Label J. Note that the Vector class has an addElement() function to dynamically insert records. Because I iteratively add to the records vector a new vector that contains an individual customer master record, the records vector ends up being a two-dimensional table (or a vector of vectors) that is a database table. After exhausting the file, I disconnect. (If I add update processing, I should do this disconnect later).

Document/View

The CustMastDataModel, the document, populates its internal buffer of records during construction. The JTable object, the view, visually presents the data contained in the document by calling functions implemented in our implementation of the AbstractDataModel class in CustMastDataModel. All of the function names of the CustMastDataModel class were inherited from the AbstractDataModel class.

There are two basic types of functions in the AbstractDataModel class: those that must be implemented and those that offer the option of overriding the default behavior of the base class’s function in the derived class. An abstract class is a class that contains a function that provides no implementation and therefore must be coded in the derived class.

AbstractDataModel has three of these: getColumnCount(), getRowCount(), and getValueAt(). The other functions implemented in CustMastDataModel were created to replace the default behavior of the functions of the same name in the AbstractDataModel class: Label K’s getColumnName() and getColumnClass().

This customer master “subfile” example doesn’t explicitly invoke any of the CustMastDataModel functions. The JTable object automatically calls these functions to present the visual view of the data model. For instance, the getColumnName() function is used by JTable to set the text for the header row of the grid. The isCellEditable() and setValueAt() functions at Labels L and M require implementation only if I want the records in the “subfile” to be modifiable. Note that CustMastDataModel’s isCell-Editable() function allows only the cells of columns zero and two to be modified—the customer name and status. The setValueAt() function typically does two things: It updates the document’s internal buffer of records with modifications from the view, and it should update the database file (which I have left out for simplicity). Remember that the view class, JTable, does not modify the data. That is the job of the document handler, CustMastDataModel. The JTable class implementation automatically invokes the setValueAt() function when a user modifies an editable column.

Required Implementations

Two of the three functions of the AbstractDataModel that must always be implemented in the derived class are the getColumnCount() and getRowCount() functions (Label N). The implementations of these are trivial in that they simply return the count of strings in the header field and number of records vector field. The other mandatory function implementation is getValueAt() shown at Label O. It returns a specific element of the records vector with a return type of Java’s generic Object type. This is the function that the JTable view uses to retrieve values for display from the data model.

The Grid View

With the CustMastDataModel instance variable, dataModel, created and its internal buffer of records populated from the DB2/400 Customer Master file, I can return to the CustMastGrid driver class to see how the grid is prepared and then placed on a window frame.

At Label E, columns 1 and 2 are resized to widths of 25 and 15 characters respectively for the customer number and status fields (remember that, in Java, arrays are indexed from 0).

At Label F, the editor for column zero, the customer name, is set to JFC’s JTextField class component. This is where you can begin to see the power of grids. A column’s assigned editor component can do an unlimited number of things. For example, the CustMastGrid application displays a check box for the Boolean value representation of customer status. But another good example component would be an extension to JTextField that edits and displays fixed decimal data and restricts entry to a range of values. Another not-so-obvious component example would be a date class that, when a date grid element is activated with a mouse click, displays a calendar graphic that allows you to visually select a date.

At the code at Label G, I finish up by creating a JFC scrollpanel, passing the JTable grid instance variable. This object will then coordinate scrolling with the JTable object. Then, after setting its height and width, I add the scrollpanel to the application’s window frame. Finally, I call the frame class’s setVisible() function, which throws the frame, along with all its components, onto the screen.

A User-ready Application

The Customer Master grid application is now ready for user manipulation. Scrolling events are handled by the JScrollPane object. JScrollPane passes requests to the JTable to redraw the view of the grid with a new set of database records. JTable retrieves those records by calling the getValueAt() function of CustMastDataModel. If a user moves focus to a new grid element, JTable asks CustMastData-Model’s isEditable() function if the column is modifiable. User changes are routed through JTable to the CustMastDataModel’s setValueAt() function.

The subfile that this Java example presents is necessarily simplistic, but I hope it gives you some insight into the powerful capabilities of grids. This grid would be comparable to a load-all subfile. The class implementation would be a little more complex for page-at-a-time or expanding subfiles. Also, I populated this grid using IBM’s AS/400 Java Toolkit, but I could have just as easily populated it with Java’s Database Connect (JDBC) classes. Look for future Midrange Computing articles about grids that use JDBC and page-at-a-time or expanding grids.

Reference

Redbook Accessing the AS/400 System with Java (SG24-2152)





Grids-_Subfiles_for_Java_Programmers06-00.png 899x268

Figure 1: The Customer Master grid

// The Java Foundation Class beta’s code name is swing
import com.sun.java.swing.*;
import com.sun.java.swing.table.*;

// standard AWT classes
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.Dimension;
import java.util.Vector;

// IBM AS/400 Java Toolkit classes
import com.ibm.as400.access.*;

// The customer master subfile driver class
public class CustMastGrid {

BEGIN LABEL A

public static void main(String[] args) { new CustMastGrid( args[0]);

}

END LABEL A

BEGIN LABEL B

public CustMastGrid(String system) {

JFrame frame = new JFrame(“Table”);

frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {System.exit(0);}});
END LABEL B

// Create a model of the data.

BEGIN LABEL C

CustMastDataModel dataModel new CustMastDataModel(system);
END LABEL C

BEGIN LABEL D

// Create the table
JTable table new JTable( dataModel);
END LABEL D

BEGIN LABEL E
// resize columns 1 and 2, leaving column 0 at default
TableColumn column1 table.getColumn(new Integer(1));
column1
.setWidth(25); TableColumn column2 = table.getColumn(new Integer(2));

column2.setWidth(15);
END LABEL E

BEGIN LABEL F

// Add JFC’s JTextField based editor for column three.
TableColumn column0 = table.getColumn(new Integer(0)); JTextField textField = new JTextField ();

textField.setBorder(null);

column0.setCellEditor(new DefaultCellEditor( textField));
END LABEL F

BEGIN LABEL G

JScrollPane scrollpane =
JTable.createScrollPaneForTable(table); scrollpane.setPreferredSize(new Dimension(400, 300));

frame.getContentPane().add( scrollpane);

frame.pack();

frame.setVisible(true);
END LABEL G

}

}

class CustMastDataModel extends AbstractTableModel {

final String[] headers = {“ Name”,”Number”,”Status”};

Vector records new Vector();
BEGIN LABEL H

public CustMastDataModel(String system) {

AS400 as400 = new AS400(system);

QSYSObjectPathName fileName = new QSYSObjectPathName (

“DENONCOURT”, “CUSTMAST”, “FILE”);

SequentialFile file = new SequentialFile(

as400, fileName.getPath());
END LABEL H

BEGIN LABEL I

AS400FileRecordDescription recDesc = new

AS400FileRecordDescription(as400,

“/QSYS.LIB/DENONCOURT.LIB/CUSTMAST.FILE”);

RecordFormat recordFormat = new RecordFormat ();
try {

recordFormat = recDesc.retrieveRecordFormat()[0];

} catch (Exception error) {

System.out.println (“get record format error:”+error);

}

try {

file.setRecordFormat( recordFormat);

} catch (Exception error) {

System.out.println (“set record format error:”+error);

}

END LABEL I

BEGIN LABEL J

// open file and populate record vector

try {

final int BlockFactor = 10;

file.open( SequentialFile.READ_ONLY,

BlockFactor,

SequentialFile.COMMIT_LOCK_LEVEL_NONE);

// populate record vector

int count = 0;

for (Record record = file.readNext();

record != null;

record = file.readNext(), count++)

{

Vector newRow = new Vector();
newRow.addElement((String)record.getField(“NAME”));
newRow.addElement( record.getField(“NUMBER”).toString());
char status =

((String )record.getField(“ACTIVE”)).charAt(0);
newRow.addElement(new Boolean(status == ‘Y’));

records.addElement( newRow);

}

as400.disconnectService(AS400.RECORDACCESS);

} catch (Exception e) {

System.out.println (“Could not read the file. Error:”+e);

}

}

END LABEL J

BEGIN LABEL K

// These methods do not have to be implemented.
public String getColumnName(int column) {
return headers[column];

}

public Class getColumnClass(int col) {
switch (col) {

case 0: return JTextField.class;

case 2: return Boolean.class;

default:
return super.getColumnClass( col);

}

}

END LABEL K

BEGIN LABEL L

public boolean isCellEditable(int row, int col) {
return (col==0 || col==2);

END LABEL L

}

BEGIN LABEL M

public void setValueAt(Object aValue, int aRow, int aColumn) {

// change the value of the internal table

// we need to add the update to the AS/400 file

Vector row = (Vector) records.elementAt(aRow);
row.setElementAt(aValue, aColumn);

}

END LABEL M

BEGIN LABEL N

// These methods always need to be implemented.
public int getColumnCount() { return headers.length; }

public int getRowCount() { return records.size();}
END LABEL N

BEGIN LABEL O

// create a scroll pane and insert the JTable to it
public Object getValueAt(int aRow, int aCol) {

Vector row = (Vector)records.elementAt(aRow);

return row.elementAt(aCol);
END LABEL O

}

}

Figure 2: The Customer Master grid Java source

Compilation Notes:

1. Download the following betas:

The AS/400 Toolbox for Java

(http://www.as400.ibm.com/products/software/javatool/javatool.htm)

The Java Foundation Class Swing beta (http://developer.javasoft.com/servlet/

SessionServlet?action=showLogin&url=/developer/earlyAccess/index.html)

2. After adding the IBM toolbox and JFC paths to the classpath, compile with this code: JAVAC.EXE CustMastGrid.java

3. Create the database file with the following SQL statement: CREATE TABLE YOURLIB/CUSTMAST +

(NAME CHAR (50), +

NUMBER INT NOT NULL PRIMARY KEY, +

ACTIVE CHAR (1) NOT NULL WITH DEFAULT ‘N’)

Don Denoncourt

Don Denoncourt is a freelance consultant. He can be reached at This email address is being protected from spambots. You need JavaScript enabled to view it..


MC Press books written by Don Denoncourt available now on the MC Press Bookstore.

Java Application Strategies for iSeries and AS/400 Java Application Strategies for iSeries and AS/400
Explore the realities of using Java to develop real-world OS/400 applications.
List Price $89.00

Now On Sale

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$

Book Reviews

Resource Center

  • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

  • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

  • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

  • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

  • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

  • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

  • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

  • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

  • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

  • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

  • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

  • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: