× The internal search function is temporarily non-functional. The current search engine is no longer viable and we are researching alternatives.
As a stop gap measure, we are using Google's custom search engine service.
If you know of an easy to use, open source, search engine ... please contact support@midrange.com.



Aldon is "fun" when working with service programs.

You tend to get the duplicate procedure error if a module is attempting to use a Binding Directory that the service program is a member of. That is bad, don't do it.
If the Service program needs to use another service program:
1. Include it directly on the creation command using keyword BNDSRVPGM((*LIBL/UTL000SP)).
2. or Create a new Binding Directory with just the service programs you need to reference.

I find it easier and more straight forward to directly specify the required service programs on the create command using keyword BNDSRVPGM((*LIBL/UTL000SP)).
That way there is no confusion as to which entries in the Binding Directory I am trying to use.

Note: Don't confuse a Binding Directory with the Export list that is referred to as the Binder Language.



When working with Aldon, the easiest way to deal with service programs and export lists is to always promote with dependent objects.
When you do, Aldon will recreate *every* program and service program that references the changed Service Program.
This works it out so you don't have to be overly worried about the export list. That is because everything is recreated and always uses the current export.


Now, if you are like us and reference the service program in *every* program, then you may not want to recompile your entire system.
With that in mind, here is a little more in depth setup for Aldon:


In the latest version (8.2a I believe) Aldon changes how they deal with Service Programs.
Prior to version 8.2a, when Promote with Dependents is selected, Aldon picks up and recreates *every* program and Service Program object that references the changed Service Program.
With version 8.2a the documentation says that they will only recreate the programs and service programs when the Export signature of the Service Program has changed.

We are still on 8.1B and have had to work around how Aldon deals with Service programs.

When promoting and deploying a Service program object, Aldon Deletes and Recreates the object.
A side note, we don't have the settings set to allow an "Update" so we don't really know if Aldon had fixed this issue.

When the service program is deleted it is gone, there is not a copy made into the QRPLOBJ Replace library. This actually causes all active programs to fail if they had previously activated the service program in their job. The jobs get the error "Tried to refer to all or part of an object that no longer exists".

As a result, that means we can only promote/install a service program during a System downtime window when we know there are no jobs using it.
We have come up with an in house workaround for this issue where we run an Update to the service program instead of having Aldon install the service program for us.

Here is the setup using Aaron Bartell's ERRORSP service program as an example.

In Aldon always add a Service Program using a Pseudo source QCLSRC member that contains the CRTSRVPGM command needed to create the service program.

In QCLSRC create member ERRORSP.CLLE:
/*============================================================================*/
/* SERVIVE PROGRAM: ERRORSP */
/*============================================================================*/
CRTSRVPGM SRVPGM(&LIB/ERRORSP) MODULE(ERRORRG) +
EXPORT(*SRCFILE) SRCFILE(QSRVSRC) +
SRCMBR(ERRORBL) TEXT('Error Handling +
- DSPSRVPGM...DETAIL(*PROCEXP)') +
ACTGRP(UTILITIES) REPLACE(*YES)

Add to Aldon:
Object "ERRORSP", Type "*SRVPGM", Source Option "2=Pseudo Source". Source File "QCLSRC".

In QCLSRC Create member ERORSPU.CLLE:
/*============================================================================*/
/* PROGRAM NAME...: ERRORSPU */
/* FUNCTION/DESC..: Update Service Program ERRORSP */
/* Program created to help facilitate correctly updating the service program.*/
/*----------------------------------------------------------------------------*/
/* MODIFICATIONS: */
/*----------------------------------------------------------------------------*/
/*============================================================================*/
PGM PARM(&P#LIB)
DCL VAR(&P#LIB) TYPE(*CHAR) LEN(10)
DCL VAR(&OBJLIB) TYPE(*CHAR) LEN(10)
DCL VAR(&LIB) TYPE(*CHAR) LEN(10)
/*----------------------------------------------------------------------------*/
/* Validate the parms */
/*----------------------------------------------------------------------------*/
IF COND(&P#LIB *EQ ' ') THEN(DO)
GOTO ENDPGM
ENDDO

IF COND(&P#LIB *EQ '*LIBL' ) THEN(DO)

RTVOBJD OBJ(ERRORSP) OBJTYPE(*SRVPGM) RTNLIB(&OBJLIB)
MONMSG MSGID(CPF0000) EXEC(DO)
GOTO ENDPGM
ENDDO
ENDDO
ELSE CMD(DO)
RTVOBJD OBJ(&P#LIB/ERRORSP) OBJTYPE(*SRVPGM) +
RTNLIB(&OBJLIB)
MONMSG MSGID(CPF9801) EXEC(DO)

CHGVAR VAR(&OBJLIB) VALUE(&P#LIB)
IF COND(%SST(&OBJLIB 1 2) *EQ 'PG') THEN(DO)
/* This section allows create requests for Developer libraries.*/
GOTO CREATEIT
ENDDO
GOTO ENDPGM
ENDDO
ENDDO
/*----------------------------------------------------------------------------*/
/* Setup Completed. */
/*----------------------------------------------------------------------------*/
IF COND(&OBJLIB *EQ ' ') THEN(DO)
GOTO ENDPGM
ENDDO
UPDSRVPGM SRVPGM(&OBJLIB/ERRORSP) MODULE(ERRORRG) +
EXPORT(*SRCFILE) SRCMBR(ERRORBL) +
ACTGRP(UTILITIES)
GOTO ENDPGM
CREATEIT:
CHGVAR VAR(&LIB) VALUE(&OBJLIB)
INCLUDE SRCMBR(ERRORSP) SRCFILE(QCLSRC)
ENDPGM:
ENDPGM

**** End of ERROSPU ****
Add to Aldon:
Object ERRORSPU, Type "*PGM", Attribute "CLLE", Source Option 1=Source, Source File QCLSRC.

Aldon is able to create the service program using the CLLE source ERRORSP.

When we don't want Aldon to recreate everything we use ERRORSPU to update the service program ourselves.
Since we are using an actually UPDSRVPGM the system is able to create the QRPLOBJ object. This means that the active jobs will continue to use the prior version of the service program until they end or are able to otherwise dereference the service program. This works well when there are only code changes to the service program. Any external changes like database changes may still cause active jobs to fail.

When we want to use the update program we make changes to the Module, Binder Language, and Prototype Copy book.
Then we promote those objects through Aldon, without recreating dependent objects.
Once the promote is complete, we run the update program.
Once the deploy install is complete, we run the update program on the remote server.


With the update program in place we are able to easily modify a service program using a few extra steps:

There are various ways to handle the Signatures in the Binder Language of a service program.
I've found the simplest solution is to follow some basic setup.

I've never found a reason to maintain more than a single signature in the service program.
When you add the *PRV entry you are just adding extra work down the line, with little benefit.

I create the binder language with a single signature:

STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('V1R1M0')
ENDPGMEXP

When using a single signature:
Always make it PGMLVL(*CURRENT).
Always give it a specific Signature String like SIGNATURE('V1R1M0')
Note the string is padded to 16 EBCDIC characters by the system when the service program is created.
The sting doesn't matter and is entirely arbitrary. Set it once and then never change it.

When using a Single Signature binder language it is Very important that you Always add new procedures to the end of the list.
Never add a new procedure to the top of the list or into the middle of the list. This goes back to the excellent explanation Mark Murphy gave concerning the "slots" for the procedures.

Changes to existing procedures generally fall into some categories.

1. Changes to the contents of a procedure that include just code changes that have no impact on the export from the service program.
If you do not change the parameters in any way, then generally the service program can be updated easily.

2. Adding new parameters: Always add new parameters to the End of the parameter list.
Doing so will allow you to continue with the current export list and only require a promotion of the Module and Prototype Copy book, followed by a call to the update program.

If you need to change the parameter list in any way, then it is a little more complicated.
The change falls into this category if you:
1. Want to reorder the parameter list.
2. Want to change size/time of any existing parameter.

Both of these changes will impact how the procedure is called.
If you are just going to Recreate all dependent objects, then you don't need to worry about these steps. Every object will use the current export list and Prototype Copy book.

If you are intending to use the Update service program method, then it is important to follow these steps.

Once a procedure has a "Slot" in the binder language file, consider it a link to the Current parameter list.
Think of that Slot as a Map to the procedure.
The system uses that map at compile time to reference the procedure.
It also uses the Prototype Copy book at compile time to determine size and attributes of the input parameters.

So, if you need to change those parameters you need to make a New Procedure.

Start by going to the old procedure and changing the name. Some people may argue against this, but it does work.

Using this Binder Language as an example:

strpgmexp pgmlvl(*current) SIGNATURE('V1R1M0')
export symbol(Error_throw)
export symbol(Error_catch)
export symbol(Error_globalErrNoTxt)
export symbol(Error_globalErrNo)
endpgmexp

Say we wanted to change the data type or size of the first parameter to "Error_Throw".

I would rename the current procedure, and then add a new procedure to the end.

strpgmexp pgmlvl(*current) SIGNATURE('V1R1M0')
export symbol(Error_throw_DEPRECATED)
export symbol(Error_catch)
export symbol(Error_globalErrNoTxt)
export symbol(Error_globalErrNo)
export symbol(Error_throw)
endpgmexp

Next I would copy the Procedure Interface for the Prior version of Error_Throw and create it with the name Error_Throw_DEPRECATED
Dcl-Proc Error_Throw_Deprecated Export;
Dcl-Pi Error_Throw_Deprecated;
pCode value Char(10);
pSeverity value Like(Error_Info.severity);
pPgm value Like(Error_Info.pgm);
pText value Like(Error_Info.text);
End-Pi;

Callp Error_throw( pCode : pSeverity : pPgm : pText );

End-Proc Error_Throw_Deprecated;

Now, sometimes you can just have the Deprecated version call the new version directly.
This will allow for easily changing the size of a parameter, but not the type.
I.e. you changed a character field from 10a to 20a. If the field is a return parm, then you may need to create a local copy of the proper length to pass into the new version. But that's just RPG coding 101.

This Deprecated procedure is Exported but generally it should not be Included in the Prototype Copy Book.
If you want to allow people to continue to use it in new programs, then you may want to consider a different naming convention like "Error_Throw_Version1" or something.


Then you code the new version of Error_Throw.
Update the module and Prototype Copy Book.
Then promote the three objects to your Production environment without dependents.
Then run the update service program.


Now, what does this setup do for you?
It seems like a lot of extra work, but what is the reward?


Well, all existing programs that are using "Error_Throw" don't need to be immediately recreated.
They continue to reference the of Error_Throw that they found in "Slot" 1 of the binder language when they were originally created.
Once the service program is updated, their calls get routed to "Error_Throw_Deprecated" automatically.

If the existing program is recreated for any reason, it immediately uses the new version of Error_Throw and the fields and attributes that are on that prototype.

This method is really useful if you are just changing the length of a parameter and not its data type.
It is less useful if you are changing the data type.


If you are changing the data type of one of the parameters then you don't need to go through all the work of adding the Deprecated procedure.
The reason is because you will have to modify all the programs that use the procedure anyway, so that they pass the proper data type.

However, you can still use the Update method because then you only have to promote the Programs that are actually using the one procedure that was changed.

In that case you would leave the binder language as it is and just modify the Program and Prototype Copy Book.
Modify all the programs that use the procedure and then promote them together.
After the promote run the update on the service program and all is well again.



The one part that doesn't change no matter how you want to promote or create your service program, is to Always add new procedures to the End of the *CURRENT export list.
If you don't follow that rule, then you just run into issues down the line.

Chris Hiebert
Senior Programmer/Analyst
Disclaimer: Any views or opinions presented are solely those of the author and do not necessarily represent those of the company.

As an Amazon Associate we earn from qualifying purchases.

This thread ...

Replies:

Follow On AppleNews
Return to Archive home page | Return to MIDRANGE.COM home page

This mailing list archive is Copyright 1997-2024 by midrange.com and David Gibbs as a compilation work. Use of the archive is restricted to research of a business or technical nature. Any other uses are prohibited. Full details are available on our policy page. If you have questions about this, please contact [javascript protected email address].

Operating expenses for this site are earned using the Amazon Associate program and Google Adsense.