Connect to multiple databases with a simple variable that detects database type and enables code to apply to a program or stored procedure written on multiple platforms.
Sometimes you need real-time integration between a PC server and a System i. It might be something simple, like accessing a file and getting a value back. Of course, that's not rocket science: an ODBC connection and a few lines of code on the PC application solve that. But what if you need more complex information? Something along the lines of a business rule validation that already exists on an RPG program or a comprehensive data set—for example, a client's debt report divided by debt age (30, 60, and 90 days) or a query to find out if a certain item exists in stock and how long retrieving it would take.
As always, there are several possibilities to tackle this. You can replicate the business logic on your PC application, create a complex view on your i database, or simply reuse existing programs and invoke them from your PC application. All you need is an ODBC connection and a few tricks.
Consider the previously mentioned client's debt report, but with a tweak: the PC application also needs to know if the client can increase its debt. In this example, that is not a simple check and the business rules are already implemented in an RPG program. We'd have to reproduce the business rules on the PC application, with all the data requirements associated with it, which is not a cost-effective solution. One workaround is to invoke an RPG program that retrieves the debt aging and also the approval or rejection for additional debt. So what are the parameters? As input, we'd need the system (test or production), the client ID, and a reference date (to calculate the aging). As output, we'd have the total debt, the debt aging, a flag with the "additional debt allowed Y/N," and a return code.
Input |
|
Parameter |
Description |
Client ID |
Client ID (shared key between the PC application and the main database) |
|
|
Output |
|
Parameter |
Description |
Total Debt |
Client's total debt |
Debt 30 Days |
Invoices outstanding up to 30 days |
Debt 60 Days |
Invoices outstanding up to 60 days |
Debt 90 Days |
Invoices outstanding 90 days or more |
Additional Debt Allowed |
Yes/No flag indicating if additional debt is allowed for this client |
Return Code |
Flag to indicate if the call was successful |
Note that the system type is purely optional. I'm mentioning it because most shops have a dedicated test system or at least a separate test library list (I'll discuss this further later).
This example uses VB and an ADO DB connection, but you can easily use other programming languages to do the same. An IBM Redbook covers this subject in depth.
Now, let's go over the code, step by step, starting with the declarations section:
Dim sCall As String
'Connect to System i
Set oCmd = New ADODB.Command
It starts by declaring a string variable, sCall, that will contain the program's name and full path, as well as its parameters. Then, it creates a new
Next, the previously mentioned parameters are added:
'set input params
Set oParam = oCmd.CreateParameter("inSystem", adChar, adParamInput, 1, mvarInSystem)
oCmd.Parameters.Append oParam
Set oParam = oCmd.CreateParameter("inClientID", adChar, adParamInput, 2, mvarClientID)
oCmd.Parameters.Append oParam
.....
Set oParam = oCmd.CreateParameter("outReturnCode", adChar, adParamOutput, 255)
oCmd.Parameters.Append oParam
sCall = sNrParams(oCmd.Parameters.Count)
Only the first two (system type and client ID) and the last (the return code) are presented. I'm not mentioning all of them because it's basically the same code, over and over.
After all the parameters are added, the structure is placed upon its final destination: the sCall string. By now, sCall contains a data structure with our parameters. The next step is to set up the call to the System i program. This call will occur without a library list, so the called System i program needs to set it up. For that, the system type parameter is relevant. If the test and production environments are in separate servers, the connection string will differ. If they're on the same server, they'll use different library lists. So the following is required to take care of that:
' Compose the program invocation command, taking into account the system type (Production or Test)
If (mvarInSystem = "P") Then
sCall = "{{CALL " & URL_LIBRARY_PROD & URL_ProgName & "(" & sCall & ")}}"
ElseIf (mvarInSystem = "T") Then
sCall = "{{CALL " & URL_LIBRARY_TEST & URL_ProgName & "(" & sCall & ")}}"
End If
URL_LIBRARY_PROD contains the name of the production library where the called program resides; URL_LIBRARY_TEST is the same for the test library.
URL_ProgName contains the name of the program to be called.
Now that the call string is complete, let's prepare the command accordingly and get the data:
'Prepare command string
oCmd.CommandType = adCmdText
oCmd.CommandText = sCall
'Get data
subConnAS400 "AS400", oCmd
This operation will fill the output parameters with the values returned by the RPG program. If everything goes well, we'll get four amounts, a yes/no flag, and a success return code ('1' means success, '0' means failure). To finalize our little example, we'll check for the return code and proceed accordingly:
mvarOutSuccess = IIf(Trim(oCmd.Parameters("outReturnCode").Value) = "1", True, False)
If mvarOutSuccess Then
'If the program ended normally, proccess the output parameters
.....
End If
To wrap it up, here's the complete code for this example:
Dim sCall As String
'Connect to System i
Set oCmd = New ADODB.Command
'set input params
Set oParam = oCmd.CreateParameter("inSystem", adChar, adParamInput, 1, mvarInSystem)
oCmd.Parameters.Append oParam
Set oParam = oCmd.CreateParameter("inClientID", adChar, adParamInput, 2, mvarClientID)
oCmd.Parameters.Append oParam
.....
Set oParam = oCmd.CreateParameter("outReturnCode", adChar, adParamOutput, 255)
oCmd.Parameters.Append oParam
sCall = sNrParams(oCmd.Parameters.Count)
' Compose the program invocation command, taking into account the system type (Production or Test)
If (mvarInSystem = "P") Then
sCall = "{{CALL " & URL_LIBRARY_PROD & URL_ProgName & "(" & sCall & ")}}"
ElseIf (mvarInSystem = "T") Then
sCall = "{{CALL " & URL_LIBRARY_TEST & URL_ProgName & "(" & sCall & ")}}"
End If
'Prepare command string
oCmd.CommandType = adCmdText
oCmd.CommandText = sCall
'Get data
subConnAS400 "AS400", oCmd
mvarOutSuccess = IIf(Trim(oCmd.Parameters("outReturnCode").Value) = "1", True, False)
If mvarOutSuccess Then
'If the program ended normally, proccess the output parameters
.....
End If
A final note about the ODBC connection: I've assumed that this part should be easy, especially if you've read the IBM documentation. But here's a small piece of code that might help:
' Make an
' ================================================================
Public Function InitEnvironment(ByVal iDBType As DBType, ByVal sAppName As String, ByVal sUserID As String, ByVal sPassw As String, ByVal sServer As String, ByVal sDatabase As String, ByVal CursorLocation As DBCursorLocation) As ADODB.Connection
On Error GoTo errHandler
Dim ConnectStr As String
Dim oConn As New ADODB.Connection
Set InitEnvironment = Nothing
oConn.CursorLocation = CursorLocation
Select Case iDBType
'SQL SERVER
Case dbSQLServer
oConn.Provider = "SQLOLEDB"
oConn.Open "Data Source=" & sServer & ";Initial Catalog=" & sDatabase & ";ApplicationFONT-FAMILY: 'Courier New'; FONT-SIZE: 10pt; mso-ansi-language: EN-GB" lang=EN-GB> Set InitEnvironment = oConn
'ORACLE
Case dbOracle
oConn.Provider = "MSDAORA.1"
oConn.CursorLocation = adUseClient
sServer = sDatabase
oConn.Open "Data Source=" & sServer & ";ApplicationFONT-FAMILY: 'Courier New'; FONT-SIZE: 10pt; mso-ansi-language: EN-GB" lang=EN-GB>
Set InitEnvironment = oConn
'AS/400
Case dbAS400
oConn.Provider = "IBMDA400"
oConn.CursorLocation = adUseClient
oConn.Open "Data Source=" & sServer & ";User ;Password=" & sPassw & ";"
Set InitEnvironment = oConn
Case Else
Set oConn = Nothing
End Select
Exit Function
errHandler:
SetError Err.Number, Err.Description & vbNewLine & "(Data Source: " & sServer & " User: " & sUserID & ")", "(InitEnvironment)"
Set oConn = Nothing
Set InitEnvironment = oConn
Exit Function
Resume
End Function
This function allows connections to multiple databases (System i, Oracle, etc.) using a simple variable, IDBType, that detects the database type. Using this, everything that was written before becomes equally applicable to a program or stored procedure written on any of the platforms mentioned on the code.
In my next TechTip, I'll explain the RPG side of this interface.
And if you need information about unidirectional interfaces (transfer a file and run a program), check out my previous tips:
- Automate Data Transfers Using FTP
- Automate FTP in "Real Life": Integrate with Lotus Domino Mail Server via LotusScript
- From a PC Text File to a System i Physical File in One or Two Easy Steps
Author's Note: I would like to thank José Romão for his assistance in writing this article.
LATEST COMMENTS
MC Press Online