Guru: IBM i, .Net, and XMLSERVICE Program and Subprocedure Calls
September 25, 2017 Richard Schoen
Our businesses have years of existing IBM i legacy applications that contain priceless business logic. We need to re-purpose that code without re-writing so our existing IBM i business logic can be accessed and used by our desktop applications, web applications, web services, and mobile device applications, which often expect JSON or XML-based data. Instead of re-writing your existing business logic, surface it so the code can be called as-is.
For this article we’ll focus on the XMLSERVICE remote program call and remote subprocedure calls that can be used to call existing logic based in existing RPG, COBOL and CL applications from .Net apps. This allows us to continue to leverage our existing business application logic in the wild as we modernize.
The same pre-requisites are needed for this article as in our previous articles. If you haven’t installed and set up the XMLSERVICE application code and created an Apache web server instance yet, please check out the introductory article before continuing, since XMLSERVICE must be up and running before our .Net application code will work. I also assume that you’ve opened, configured, and run the test application at least once.
Running The Test CL Program Call
Go ahead and launch Visual Studio, then load and open the XmlServiceApi solution file C:\XmlServiceiApi\XmlServiceiApi.sln. Locate the XmlServiceApiTester project, then double-click FormMain.vb from the Solution Explorer window to open the main form.
If you have configured things correctly, you should be able to click the play button or F5 to make the API tester application run and display its application window. To test the remote program call functionality in the API, click the “Run Program” button to run the sample CL program TESTCL1 contained in IBM i library XMLDOTNETI (assuming you’ve loaded the library). The program call should complete quickly, and you should see a response value of Thank you for your business and a second value of 1000100.22 in the return parameters.
The run program functionality works just like a call with parameters from a CL or RPG program, so your .Net apps can call any application on your IBM i that you have permission to execute. The first parameter was sent into the CL program as a blank field, and a text response message value was returned from the program call. For the second numeric value, an initial value was passed in of 100.00 and it was incremented by 1000000.22 to illustrate manipulating and returning a numeric value of 15.2 packed format. We won’t be analyzing any CL or RPG code for this article but look in the XMLDOTNETI library to see how simple the code is for program TESTCL1.
Running The RPG Service Program Procedure
If you click the “Run Procedure” button you will call a sample RPG subprocedure named sample1, which is contained in service program XMLSVC001 in library XMLDOTNETI. The sample1 test subprocedure manipulates the incoming parameter, returning a new value – the original value concatenated with the words “return value.” It’s an easy way to illustrate the general plumbing needed for calling a subprocedure housed within an RPG-based service program via XMLSERVICE.
The idea of being able to call existing programs and service program code allows you to create new business logic that can be used with any existing RPG or COBOL application and can also be called from remote applications written using .Net and other languages with the same result. All of your re-usable IBM i business logic remains securely hosted in one place on the IBM i system.
Reviewing The Code For A CL Program Call
Let’s take a quick look at the code and an overview of what happens when you run the remote CL program and subprocedure. First you must set up connectivity parameters. To set up for any call to the IBM i, use the SetBaseURL function to set the base URL into the XMLSERVICE object from the test project settings. Next the SetUserInfo function sets the desired IBM i user and password from the settings. In a real application, you might ask the user to enter a user ID and password value or store a service account for the IBM i app.
Next the SetIpcInfo function sets the unique IPC conversation for this connection to connect it to the associated IBM i job that allows job persistence. Then the SetHttpTimeout function is used to determine how many seconds the HTTP connection will run before it times out. I normally use 30 seconds for a transaction timeout. However, you can set it higher or lower depending on your expected transaction length. You don’t want users to perceive that an application has locked up.
After all the connection setup work is done, you will create a parameter list array for the two parameters being passed to the TESTCL1 program and use the ExecuteProgram function to call the program from library XMLDOTNETI. The function returns true if the program call completed normally, or false if something failed.
It’s important to note that when you call CL or RPG programs and subprocedures from a remote application, you will need to make sure your IBM i code handles any errors within the RPG, CL or COBOL app code. If you don’t handle code errors, your IBM i code may throw a CPF exception error message to the QSYSOPR message queue. To the user this can appear as if their .Net application has locked up, when in fact it’s waiting for a response from the system operator. The HTTP server will time out eventually, but it’s not nice for your apps called by XMLSERVICE to throw a hard halt as needed, so code diligently with your IBM i code and anticipate errors. Properly handling remote code call errors is not unique to .Net. The same problem can occur when using PHP, Java, Ruby or RPG applications to call commands remotely.
Let’s look at the VB code behind the Run Program Command button click event to see how easy it is to use the XMLServicei data access object to call programs with parameters. Double-click on FormMain to open the designer window or right click on FormMain.vb and select View Code to open the code window. If you look at the code behind for FormMain.vb, you will first see that we import the XMLServiceiApi namespace (Imports XMLServiceiApi) so that we don’t have to prefix our code calls with XMLServiceiApi. That would get tedious. Since this is a desktop application we create a variable called _xmlService (Dim _xmlService As New XmlServicei), which gets initialized at program startup with our connection information so we can re-use the same connection while the Windows application is active. For web applications, this info would possibly get stored in the web session or perhaps instantiated for each page call, since XMLSERVICE connections are stateless.
To access the Run Program code, double-click the Run Program Command button to open the ButtonProgram_Click subroutine. You will notice that I like to wrap all of my code calls with the Try/Catch routine to handle errors. This is similar to the MONMSG or MONITOR/ENDMON in CL and RPG. You will also notice that SetBaseURL, SetUserInfo, SetIpcInfo, and SetHttpTimeout are all called to set up the connection and login info from the app config. Normally you would prompt the user to enter those settings. Then you see the single call to the ExecuteProgram function to run the TESTCL1 CL program. After the CL program call completes the GetDataTableProgramResponse function is called to bring back the parameter responses in .Net DataTable format so they can be displayed in a grid. Then GetLastXmlResponse gets called so we can display or process the entire XML response string from XMLSERVICE if desired. Using the XmlServiceiApi it’s this simple to call programs from a.Net application.
If you’re feeling super adventurous, you can also open up XmlServicei.vb source and take a look at the ExecuteProgram code. You will see that the function composes an XML data stream to post to the XMLSERVICE xmlcgi program via HTTP. It then processes the returned XML responses. There’s very little .Net communications plumbing code required since XMLSERVICE does most of the heavy lifting work on the IBM i side and we just call it via HTTP or HTTPS for secure calls.
Reviewing The Code For RPG Subprocedure Call
Let’s take a quick look at the code that runs the RPG subprocedure. The connectivity setup is the same as in the program call example.
After the setup work is done, you will create a parameter list array for the input and return parameters that are passed to the subprocedure call, so it’s very close to the program call sample in functionality. After creating the parameter lists, you will use the ExecuteProgramProcedure function to call the sample1 subprocedure in service program XMLSVC001 in library XMLDOTNETI. The function returns true if the call completed normally. Otherwise it returns false, which means something failed.
To access the Run Program code, double-click the Run Procedure Command button to open the ButtonProcedure_Click subroutine. The setup code is called for the connection and parameter lists, and then you see the single call to the ExecuteProgramProcedure function to run the sample1 subprocedure. After the call completes the GetDataTableProgramResponse function is called to bring back the parameter responses in .Net DataTable format so they can be displayed in a grid. Then GetLastXmlResponse gets called so we can display or process the entire XML response string from XMLSERVICE if desired. Just as in calling a CL or RPG program, it’s this simple to call RPG subprocedures from a.Net application.
This completes my initial series on using XMLSERVICE with .Net. I recently moved this project to my GitHub repository since that seems to be the thing to do these days for open source projects. During the move I ported the project source to the new .Net Standard format, which allows the DLL assembly to be used with both standard .Net projects for Windows, but also with .Net Core 2.0 multiplatform projects which can run on Windows, Linux and MacOS. I also created a nuget package called IbmiXmlserviceStd that can be used to add the latest compiled DLL to any .Net project in seconds.
I’m not counting on IBM to release a .Net core appropriate driver for Windows and Linux, so XMLSERVICE may be the answer for communications for all of your .Net Core apps to IBM i. Keep an eye on the GitHub repo for other new .Net and IBM i related projects as well. If you have any suggestions for IBM i or .Net related topics or examples, please let me know.