iSeries EGL

All things EGL for the iSeries / i5 / Power System

Calling a CL Program Using PCML

leave a comment »

Introduction

This post shows how to set up a call from EGL to a CL or RPG program residing on the iSeries. It employs a PCML solution using Java. The parts to be developed are

  • the EGL External Type which prototypes the Java methods of the Java program so that EGL can call them.
  • the PCML definition that describes the parameters to be passed to the CL or RPG program.

To get a better understanding about PCML, please refer to this reference material from IBM.

The example that follows is very simple in that the program being called does not modify the variables passed to it which would affect how the PCML program definition XML is coded.

This post divides the two areas of responsibility into sections for the Java code providing PCML support and the EGL service code that interfaces with the Java code. The first section is presented below.

Code required on the Java side of the landscape

This section illustrates the Java code and supporting PCML file that need to be supplied to the project. The exception is the Java class called BasePCML which is defined in a .jar-file to be included into your EGL project.

Setting up the interface using PCML

Shown below is the PCML definition. It’s an XML file. The entry program name=”SYSLOGC1″ declares the name of this PCML declaration for the program residing on the iSeries. The program’s location is described with the path statement that follows the program name. The entries that follow describe the name, type, length of each parameter to be passed to the program. The entry usage=”input” indicates the variable is input-only and won’t be returned to the caller.

<pcml version="1.0">
  <program name="SYSLOGC1" path="/QSYS.lib/INSOLIB.lib/SYSLOGC1.pgm">

      <data name="USERID" type="char" length="10" usage="input" />
      <data name="PGMNAME" type="char" length="30" usage="input" />
      <data name="FUNCNAME" type="char" length="50" usage="input" />
      <data name="EVENTID" type="char" length="08" usage="input" />
      <data name="LOGGINGLEVEL" type="char" length="10" usage="input" />
      <data name="MESSAGE" type="char" length="256" usage="input" />

  </program>
</pcml>

Fig.1 – The PCML definition for the iSeries program to be called.

 

The Base Java Class that Supports Program Calls Using PCML

The Java program shown below is the base class supporting PCML program calls. The generic nature of this class lends itself well for reuse and can best serve the development staff by being packaged into a .jar-file. The post at this link shows how this process is accomplished, but for now just know that this class has been through that process and the resulting .jar-file should be included in your project.

package com.app.pcml;

import java.io.IOException;
import java.math.BigDecimal;

import org.apache.log4j.Logger;

import com.ibm.as400.access.AS400;
import com.ibm.as400.access.AS400Message;
import com.ibm.as400.access.Trace;
import com.ibm.as400.data.PcmlException;
import com.ibm.as400.data.ProgramCallDocument;
 

/**
 * 
 * This is the Base class for all PCML based calls.  Classes should extend this class  
 * use the facilities provided here to call programs residing on the iSeries.
 * 
 * This class will be rarely modified, except for fixes and/or enhancements.
 * 
 * @author 
 *
 */
public class BasePCML {

	
	// use for PCML Tracing
	public static final boolean TRACE_PCML_ON = true;
	public static final boolean TRACE_PCML_OFF = false;
	
	private static boolean TRACE_PCML_SETTING = false;
	private static String TRACE_PCML_FILE = null;
	private String pcmlPathAndFileName = null;
	
	
	private AS400 as400 = null;
	
	//PCML document describing parameters
	private ProgramCallDocument pcmlDoc = null;
	
	// Return code from ProgramCallDocument.callProgram()
	private boolean returnCode = false;

	// Messages returned from the server
	String msgId, msgText;  
	
	String pcmlDocPackageName = null;
	
	/**
	 * 
	 * Constructor
	 * 
	 */
	public BasePCML(String mach, String user, String pswd, String pcmlDocPackageName) {
		as400 = new AS400(mach, user, pswd);

		try {
			
			pcmlDoc = new ProgramCallDocument(as400,pcmlDocPackageName);
 			
		} catch (PcmlException pcmle) {
			
			System.out.println(pcmle.getLocalizedMessage());	
			
		}
	}
	
 
	
	
	/**
	 * 
	 * When accessing values from the ProgramCallDocument class, you must
	 * specify the fully qualified name of the document element or  tag.
	 * The qualified name is a concatenation of the names of all the containing
	 * tags with each name separated by a period. EX: SYSSECUSER.parm1.ADUSERID
	 * 
	 * @param adUserId
	 */
	protected void setParm(String parmID, String value)  {
		
		try {
		
			pcmlDoc.setValue(parmID, value);
		
		} catch (PcmlException pcmle) {
			System.out.println ("Setting " + parmID + " value " + value + " failed.");
			System.out.println(pcmle.getLocalizedMessage());
			System.out.println(pcmle.getMessage());
			pcmle.printStackTrace();
		}
	} 
	

	/**
	 * Set a String value in the PCML document.
	 * @param field java.lang.String
	 * @param value java.math.BigDecimal
	 * @param indices int[]
	 */
	public void setParm(String field, String value, int[] indices) {
		
		if (value != null) {
			try {
				// Set the value in the PCML document
				pcmlDoc.setValue(field, indices, value);
			}
			catch (PcmlException e) {
				System.out.println("PCML Exception in PCMLRequest::setValue");
				System.out.println(e.getLocalizedMessage());
				System.out.println(e.getMessage());
				e.printStackTrace();
			}
			catch (Exception e) {
				System.out.println("Exception in PCMLRequest::setValue");
				System.out.println(e.getMessage());
				e.printStackTrace();
			}
		}
	}
	
	
	/**
	 * Set a BigDecimal value in the PCML document.
	 * @param field java.lang.String
	 * @param value java.math.BigDecimal
	 */
	public void setParm(String field, BigDecimal value) {
		if (value != null) {
			try {
				// Set the value in the PCML document
				pcmlDoc.setValue(field, value);
			}
			catch (PcmlException e) {
				System.out.println("PCML Exception in PCMLRequest::setValue");
				System.out.println(e.getLocalizedMessage());
				System.out.println(e.getMessage());
				e.printStackTrace();
			}
			catch (Exception e) {
				System.out.println("Exception in PCMLRequest::setValue");
				System.out.println(e.getMessage());
				e.printStackTrace();
			}
		}
	}
	

	
	/**
	 * Set a BigDecimal value in the PCML document.
	 * @param field java.lang.String
	 * @param value java.math.BigDecimal
	 * @param indices int[]
	 */
	public void setParm(String field, BigDecimal value, int[] indices) {
		if (value != null) {
			try {

				// Set the value in the PCML document
				pcmlDoc.setValue(field, indices, value);
			}
			catch (PcmlException e) {
				System.out.println("PCML Exception in PCMLRequest::setValue");
				System.out.println(e.getLocalizedMessage());
				System.out.println(e.getMessage());
				e.printStackTrace();
			}
			catch (Exception e) {
				System.out.println("Exception in PCMLRequest::setValue");
				System.out.println(e.getMessage());
				e.printStackTrace();
			}
		}
	}
	
	
	/**
	 * 
	 *  Call the program
	 *  
	 */
	protected void callProgram(String programName) {
		
		try {
			
			returnCode = pcmlDoc.callProgram(programName);
			
            if(returnCode == false)              {
                // Retrieve list of server messages
                AS400Message[] msgs = pcmlDoc.getMessageList(programName);
                
                // Iterate through messages and write them to standard output
                System.out.println("**********************************************************");
                System.out.println("**  " + programName + " failed. See messages below               **");
                for (int m = 0; m < msgs.length; m++)                  {
                    msgId = msgs[m].getID();
                    msgText = msgs[m].getText();
                    System.out.println("    " + msgId + " - " + msgText);
                }
                
            }
            
		
		} catch (PcmlException pcmle) {
			System.out.println("Call to SYSSECUSER failed.");
			System.out.println( pcmle.getMessage() );
		}
		
	}
	
	
	/**
	 * Return a String value from the PCML document. 
	 * 
	 * @return java.lang.String
	 * @param parmName
	 *            java.lang.String
	 */
	protected String getParm(String parmName) {
		
		try {
			
			return (String) pcmlDoc.getValue(parmName);
			
		} catch (PcmlException e) {

			System.out.println("PCML Exception in PCMLRequest::getString");
			System.out.println(e.getLocalizedMessage());
			System.out.println(e.getMessage());

			e.printStackTrace();
		} catch (Exception e) {
			System.out.println("Exception in PCMLRequest::getString");
			System.out.println(e.getMessage());
		}
		
		//Returning a zero-length string
		return "";
	}

	/**
	 * Return a String value from the PCML document.
	 *  
	 * @return java.lang.String
	 * @param field java.lang.String
	 * @param indices int[]
	 */
	public String getString(String field, int indices[]) {
		try {
			return (String) pcmlDoc.getValue(field, indices);
		}
		catch (PcmlException e) {
			System.out.println("PCML Exception in PCMLRequest::getString");
			System.out.println(e.getLocalizedMessage());
			System.out.println(e.getMessage());
			e.printStackTrace();
		}
		catch (Exception e) {
			System.out.println("Exception in PCMLRequest::getString");
			System.out.println(e.getMessage());
		}
		return null;
	}
	
	
	/**
	 * Return a BigDecimal value from the PCML document.
	 * 
	 * @return java.math.BigDecimal
	 * @param field java.lang.String
	 */
	public BigDecimal getBigDecimal(String field) {
		try {
			return (BigDecimal) pcmlDoc.getValue(field);
		}
		catch (PcmlException e) {
			System.out.println("PCML Exception in PCMLRequest::getBigDecimal");
			System.out.println(e.getLocalizedMessage());
			System.out.println(e.getMessage());
			e.printStackTrace();
		}
		catch (Exception e) {
			System.out.println("Exception in PCMLRequest::getBigDecimal");
			System.out.println(e.getMessage());
		}
		return null;
	}


	/**
	 * Return a BigDecimal value from the PCML document.
	 *  
	 * @return java.math.BigDecimal
	 * @param field java.lang.String
	 */
	public BigDecimal getBigDecimal(String field, int[] indices) {
		try {
			return (BigDecimal) pcmlDoc.getValue(field, indices);
		}
		catch (PcmlException e) {
			System.out.println("PCML Exception in PCMLRequest::getBigDecimal");
			System.out.println(e.getLocalizedMessage());
			System.out.println(e.getMessage());
			e.printStackTrace();
		}
		catch (Exception e) {
			System.out.println("Exception in PCMLRequest::getBigDecimal");
			System.out.println(e.getMessage());
		}
		return null;
	}
	
	
	/**
	 * 
	 */
	public void disconnectAS400() {
		as400.disconnectService(AS400.COMMAND);
	}	
	
}


Fig.2 – The Java class ‘BasePCML’. The developer does not need to code this!!!

 

The Java Class that will call the iSeries Program

This class extends the base class shown above. It is tailored to call a specific program.

package com.app.pcml;

/**
 * 
 * This class extends BasePCML to facilitate the execution of program
 * / residing on the iSeries.
 * 
 * All processes associated with the program to be called will be encapsulated
 * in methods of this class.
 * 
 * @author  
 *
 */
public class CallSYSLOGC1 extends BasePCML {
	
        //The name of the program being called
	public static final String PROGRAM_NAME = "SYSLOGC1";
	
	//Package-file name of the Program interface defined using PCML
	private static final String PCML_DOC_PACKAGE_NAME = "com.app.pcml.SYSLOGC1.pcml";
	
	//Parm related literals referencing the parm-names in the PCLM file
	public static final String USER_ID 			= "SYSLOGC1.USERID";
	public static final String LOGGING_PROGRAM_NAME		= "SYSLOGC1.PGMNAME";
	public static final String FUNCTION_NAME		= "SYSLOGC1.FUNCNAME";
	public static final String EVENT_ID			= "SYSLOGC1.EVENTID";
	public static final String LOGGING_LEVEL		= "SYSLOGC1.LOGGINGLEVEL";
	public static final String MESSAGE			= "SYSLOGC1.MESSAGE";
	
	//Known error message(s) returned in parm OUT_VALUE
	public static final String ERROR_MESSAGE_01 = " ";
	public static final String ERROR_MESSAGE_02 = " ";		
	
	/**
	 * Constructor
	 * 
	 * Obtains connection criteria to the iSeries to enable program execution
	 * in addition to passing to the parent class the Package-file name of the 
	 * Program interface defined using PCML.
	 * 
	 */
	public CallSYSLOGC1( String mach, String user, String pswd ) {
		 
		super(mach, user, pswd, PCML_DOC_PACKAGE_NAME);
		
	}	

	
	/**
	 *  
	 * Write entry into file SYSTLMP which is the log file on the iSeries. 
	 * 
	 * @param activeDirectoryName
	 * @return String iSeriesUser
	 * 
	 */
	public void log(String userID, String pgmName , String functionName, String eventID, String loggingLevel, String message ) {
		
		//Set the parms
		super.setParm(USER_ID, userID);
		super.setParm(LOGGING_PROGRAM_NAME, pgmName);
		super.setParm(FUNCTION_NAME, functionName);
		super.setParm(EVENT_ID, eventID);
		super.setParm(LOGGING_LEVEL, loggingLevel);
		super.setParm(MESSAGE, message);
		
		//Call
		super.callProgram(PROGRAM_NAME);
		
 		return;
	}
}

Fig.3 – The Java class that calls the CL program on the iSeries. CallSYSLOGC1.java

 
The developer needs to provide values as they relate to the program that needs to be called. The entries are listed here.

  1. //The name of the program being called public static final String PROGRAM_NAME = "SYSLOGC1";
  2. //Package-file name of the Program interface defined using PCML private static final String PCML_DOC_PACKAGE_NAME = "com.app.pcml.SYSLOGC1.pcml";
  3. //Parm related literals referencing the parm-names in the PCLM file public static final String USER_ID = "SYSLOGC1.USERID"; public static final String LOGGING_PROGRAM_NAME = "SYSLOGC1.PGMNAME"; public static final String FUNCTION_NAME = "SYSLOGC1.FUNCNAME"; public static final String EVENT_ID = "SYSLOGC1.EVENTID"; public static final String LOGGING_LEVEL = "SYSLOGC1.LOGGINGLEVEL"; public static final String MESSAGE = "SYSLOGC1.MESSAGE";
  4. //Java Method public void log(String userID, String pgmName , String functionName, String eventID, String loggingLevel, String message ) { //Set the parms super.setParm(USER_ID, userID); super.setParm(LOGGING_PROGRAM_NAME, pgmName); super.setParm(FUNCTION_NAME, functionName); super.setParm(EVENT_ID, eventID); super.setParm(LOGGING_LEVEL, loggingLevel); super.setParm(MESSAGE, message); //Call super.callProgram(PROGRAM_NAME); return; }

Fig.4 – The entries in the Java program unique to each program called.

List item 1. establishes the program name.

Item 2. describes the package name within which the PCML file resides. Note the names are not separated by slashes (“/”) as usual. This is a departure for the norm and is required in this case.

Item 3. describes the list of parameters as defined by the PCML file seen in Fig.1.

Item 4. describes the name of the method that will be called by the EGL program. Note that the parameters passed to the method match the parameters defined by the PCML definition shown in Fig.1.

Code required on the EGL side of the landscape

This section illustrates the EGL code that need to be supplied to the project.

 

The EGL code

The first thing that needs to be coded is the EGL External Type that describes the constructor of the class and the Java method that is to be called by the EGL service.

Shown below is the EGL External Type. ExternalType parts map EGL to external language elements. In this case. we’re mapping the constructor and the methods of the Java class called CallSYSLOGC1.java presented above in Fig.3.

package com.mig.logviewer.externaltypes;

externalType CallSYSLOGC1 type JavaObject{PackageName = "com.app.pcml"}
    
    constructor(mach String in,  user String in, pswd String in );
    function log(USERID String in, PGMNAME String in,  FUNCNAME String in, EVENTID String in, LOGGINGLEVEL String in,  MESSAGE String in) ;
        
end

Fig.5 – The External Type “SYSLOGC1.egl”

 

Shown below is the EGL service code that instantiates a new object (syslogc1) that is derived from the Java class named CallSYSLOGC1.java which has been mapped by the EGL External Type (SYSLOGC1.egl).

       
        syslogc1 CallSYSLOGC1 =  new CallSYSLOGC1("S1039255", "EGLCONNECT", "eg1c0nn3c7");
        user string = "None";
        programName string = "LoggingViewer";                               //30 bytes
        functionName string = "handler: getRuntimeEnvironmentReturn()";     //50 bytes
        eventID string = "";                                                //08 bytes
        loggingLevel string = "DEBUG     ";                                      //10 bytes
        message string = "Runtime Environment Variable for server is '" + runtimeEnvironment + "'";
        syslogc1.log(user, programName, functionName, eventID, loggingLevel, message);

Fig.6 – The EGL code that calls the method (log)in CallSYSLOGC1.java”

Summary

This post has shown how to use PCML in an EGL application to call a program residing on the iSeries. It uses EGL’s external type to map to a Java class included into the EGL project which is then called by the EGL service program.

 

Advertisements

Written by iseriesadmin

June 29, 2012 at 1:45 pm

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: