Understanding the %metadata Application Package

The %metadata application package in PeopleSoft is very intriguing.  This app package is unlike any other as we (PeopleSoft Developers) do not have access to the implementation of this package.  When you try to open this package in App Designer, the IDE acts as if the package does not exist.  However, if you correctly reference this package’s contents (sub-packages, classes, methods, properties, etc.) then App Designer does not bat an eye.  It should be well understood that (our) usage of this package is not supported by Oracle as it is undocumented.  However, there are currently no measures in place to prevent blind usage this app package.  While I definitely do not advise the usage of this package in any legitimate PeopleSoft system, I thought it would be a fun educational exercise to try to understand this mysterious app package.

I would like to start this off by making it clear that none of the information I am presenting in this post should be treated as factual.  This is merely my understanding an obscure and undocumented PeopleTools technology.

References to the %metadata package can be seen in some of the delivered code.  From what I can gather, the delivered code uses this package for accessing/manipulating PeopleTools-managed objects.  This package offers a (potentially safer) alternative to accessing/manipulating PeopleTools objects through the metadata tables in the database.

 

Overview of %metadata

There are three major aspects to understand when using the %metadata package.

  • Definitions
  • Managers
  • Keys

Definitions can be thought of as the object types.  So if you want to work with a record object type, then it would be referenced with %metadata as follows:

/* Record Definition */
Local %metadata:RecordDefn:RecordDefn &oRecordDefn;

In order to obtain a reference to an object definition, you must use the manager for the given object type. Managers are responsible for fetching  and creating object definitions.  So to work with a record definition, the record definition manager must first be instantiated as follows:

 /* Record Manager */
 Local %metadata:RecordDefn:RecordDefn_Manager &oRecordDefn_Manager;
 &oRecordDefn_Manager = create %metadata:RecordDefn:RecordDefn_Manager();

In order for the manager to supply a reference to an existing object definition, you will have to supply the manager with the key to the definition.  Keys are name-value pairs that reference a single definition of a given object type.  So if we want to manage a record named “PSM_TEST”, then the key would be created as follows:

 /* Example Key Object representing a record */
 Local %metadata:Key &oRecordKey;
 &oRecordKey = create %metadata:Key(Key:Class_Record, "PSM_TEST");

The key object instantiation is rather strange (syntax-wise) and I will get into more detail on this later.  But for now, we can feed the generated key object to the record manager’s GetDefn method, to obtain a non-updateable reference to the PSM_TEST record object definition.

/* Get reference to a non-updateable record definition */
&oRecordDefn = &oRecordDefn_Manager.GetDefn(&oRecordKey);

If you want to make changes to the record definition, then you can obtain an updatable version of the definition by supplying the generated key to the manager’s GetDefnToUpdate method.

/* Get reference to an updatable record definition */
&oRecordDefn = &oRecordDefn_Manager.GetDefnToUpdate(&oRecordKey);

As I said before, managers are capable of creating new object definitions.  The key is not needed for creating a new object.  The manager’s CreateDefn method will return a reference to a new (empty) record definition.

/* Get reference to a new record definition */
&oRecordDefn = &oRecordDefn_Manager.CreateDefn();

Now that we have a high level overview of the important pieces to using %metadata, I would like to go into more granular detail of each of the pieces so that we can understand how to actually manipulate PeopleTools-managed objects.

 

Understanding %metadata Definitions

%metadata has numerous sub-packages that  each represent a PeopleTools-managed object type.  I gave an example of accessing the Record object type above by referencing the RecordDefn sub-package. Many (if not all) object types can be referenced with %metadata and they are all referenced by a somewhat guessable sub-package naming scheme.  Here are some examples:

 /* Example Definition Objects */
 Local %metadata:MenuDefn:MenuDefn &oMenuDefn;
 Local %metadata:ComponentDefn:ComponentDefn &oComponentDefn;
 Local %metadata:PageDefn:PageDefn &oPageDefn;
 Local %metadata:PeopleCodeProgram:PeopleCodeProgram &oPeopleCodeProgram;
 Local %metadata:AppPackageDefn:AppPackageDefn &oAppPackageDefn;
 Local %metadata:RoleDefn:RoleDefn &oRoleDefn;
 Local %metadata:PermissionListDefn:PermissionListDefn &oPermissionListDefn;

The key takeaway to understanding these objects is that they essentially represent their database “*DEFN” table counterpart.  For example,  in order to understand the %metadata Record Definition, you must first understand the structure of the PSRECDEFN table in the database.  The properties of the Record Definition object should, more or less, reflect the fields that are on the PSRECDEFN table.

 

Understanding %metadata Managers

I have found that each %metadata object definition package contains a manager class.  The exact name of the manger class for any given object definition seems to be the object definition name with “_Manager” appended to it.   As we saw earlier the manger class name for the RecordDefn Record object definition was RecordDefn_Manager.  Here are some examples of other types of managers:

 /* Example Manager Objects */
 Local %metadata:MenuDefn:MenuDefn_Manager &oMenuDefn_Manager;
 Local %metadata:ComponentDefn:ComponentDefn_Manager &oComponentDefn_Manager;
 Local %metadata:PageDefn:PageDefn_Manager &oPageDefn_Manager;
 Local %metadata:PeopleCodeProgram:PeopleCodeProgram_Manager &oPeopleCodeProgram_Manager;
 Local %metadata:AppPackageDefn:AppPackageDefn_Manager &oAppPackageDefn_Manager;
 Local %metadata:RoleDefn:RoleDefn_Manager &oRoleDefn_Manager;
 Local %metadata:PermissionListDefn:PermissionListDefn_Manager &oPermissionListDefn_Manager;

All of the manager classes seem to have a common set of methods for getting and creating their respective object definitions.  I demonstrated earlier the GetDefn, GetDefnToUpdate, and CreateDefn methods for the Record Definition manager class.  I have found that these methods are defined in manager classes of other object definitions as well.  Along with these three methods are a coulple of other commom methods: DefnExists and GetPrivateDefn.  I am unsure of use cases for the GetPrivateDefn method, but the DefnExists method can be use to determine if an object defnition exists for a provided %metadata:key.

 

Understanding %metadata Keys

The keys are used to reference a specific definition for a given object type.  The key object is a required parameter for all manager class’s GetDefn, GetDefnToUpdate, and DefnExists mehods. The instantiation of a key object is rather strange as the key constructor behaves in an overloaded fashion and takes a non-PeopleCode object type as a parameter.  This can be proven by trying to extend the Key object’s constructor in App Designer:

Key_Constructor

As you can see from the picture above, the key’c constructor takes a RepeatedAny object type, which is not a native PeopleCode object type.  This leads me to believe that we are directly referencing a lower-level language (non-PeopleCode) implementation of these (%metadata) objects.  This could be one of the reasons why we do not have access to view the source of the %metadata package in App Designer.

Aside from its odd syntactical references, the key object is fairly straight-forward to understand. The %metadata:key class contains numerous sub-class, integer constants.  Each of the integer constants represent a specific object type. As we saw earlier, I used the Class_Record sub-class to represent the Record Definition object type.  Here are some examples of how to reference other available key sub-classes:

 /* Example Key sub-class, integer constants */
 Local integer &iMenuObjType = Key:Class_Menu;
 Local integer &iComponentObjType = Key:Class_PanelGroup;
 Local integer &iMarketObjType = Key:Class_Market;
 Local integer &iPageObjType = Key:Class_Panel;
 Local integer &iMethodObjType = Key:Class_Method;

The integers returned by these sub-classes are the key in the key-value pairs to construct a %metadata:key to give to a manager for an object definition.  The value in the key-value pair is simply a string.   Earlier I inputted the string “PSM_TEST” to refer to a record definition of a record named PSM_TEST.  To further solidify our understanding of this concept, I would like to provide another example of instantiating a key.  This time, I want to refer to a Component-level PeopleCode Program object. Specifically,  I will reference the PostBuild event to the USERMAINT Component:

 /* Example Key Object representing a PeopleCode Program */
 Local %metadata:Key &oKey = create %metadata:Key(Key:Class_PanelGroup, "USERMAINT", Key:Class_Market, "GBL", Key:Class_Method, "PostBuild");

A similar way to generate keys is to use the AddItem method.  This is my preferred way to generate keys as it is a bit easier to read and understand.  Here is an example of using AddItem to generate a key that references the same Component-level PeopleCode Program object as in the previous example:

 /* Instantiate the Key object */
 Local %metadata:Key &oKey = create %metadata:Key();
 
 /* Add the Component key-value pair */
 &oKey.AddItem(Key:Class_PanelGroup, "USERMAINT");
 
 /* Add the Market key-value pair */
 &oKey.AddItem(Key:Class_Market, "GBL");
 
 /* Add the Event key-value pair */
 &oKey.AddItem(Key:Class_Method, "PostBuild");

The knowledge of which key-value pairs are needed to reference a particular object type can be derived a couple of different ways.  The first way is to consider the key fields of the corresponding “*DEFN” database table of the object type that you are referencing.  The second way is to simply think of what input values App Designer requires for you to view a particular object type.

 

A Complete %metadata Example

I would now like to provide a complete example of using the %metadata application package. In this example, I will be editing the PeopleCode defined in the PostBuild event of the USERMAINT component.

import %metadata:Key;
import %metadata:PeopleCodeProgram:PeopleCodeProgram_Manager;
import %metadata:PeopleCodeProgram:PeopleCodeProgram;

/* Instantiate the Key object */
Local %metadata:Key &oKey = create %metadata:Key();

/* Add the USERMAINT Component key */
&oKey.AddItem(Key:Class_PanelGroup, "USERMAINT");

/* Add the GBL Market key */
&oKey.AddItem(Key:Class_Market, "GBL");

/* Add the PostBuild Event Name key */
&oKey.AddItem(Key:Class_Method, "PostBuild");

/* Instantiate the PeopleCode Program Manager object */
Local %metadata:PeopleCodeProgram:PeopleCodeProgram_Manager &oManager = create %metadata:PeopleCodeProgram:PeopleCodeProgram_Manager();

/* Determine if a PeopleCode Program Definition exists for the given key */
Local boolean &bExists = &oManager.DefnExists(&oKey);

/* Throw an exception if the definiton does not exists */
If Not (&bExists) Then
 throw CreateException(0, 0, "Definition does not exist for the provided key");
End-If;

/* Get the PeopleCode Program Definition */
Local %metadata:PeopleCodeProgram:PeopleCodeProgram &oPeopleCodeProgram = &oManager.GetDefnToUpdate(&oKey);

/* Get the PeopleCode that is definied for the loaded PeopleCode Program */
Local string &sProgram = &oPeopleCodeProgram.GetProgram();

/* Append a mesasagebox to the obtained PeopleCode string */
&sProgram = &sProgram | "messagebox(0,"""",0,0, ""Modifying PeopleCode with PeopleCode!"");";

/* Update the PeopleCode for the Peoplecode Program */
Local any &test1, &test2, &test3;
Local boolean &bUpdatedPeopleCode = &oPeopleCodeProgram.UpdateProgram(&sProgram, &test1, &test2, &test3);

/* Throw an exception if the PeopleCode did not update */
If Not (&bUpdatedPeopleCode) Then
 throw CreateException(0, 0, "PeopleCode did not update");
End-If;

/* Update the PeopleCode Program Definition to save the change to the PeopleCode */
Local boolean &bDefnUpdated = &oPeopleCodeProgram.UpdateDefn();

/* Throw an exception if the definition did not update */
If Not (&bDefnUpdated) Then
 throw CreateException(0, 0, "Definition did not update");
End-If;

 

Another Complete %metadata Example (Updated: 9/4/17)

I received a comment below from Jason about building Role definitions with %metadata.  I think this is a good use case of this package as it can allow a developer to administer PeopleSoft Security in an automated, object-oriented fashion.  Below is an example of how to use %metadata to assign a Permission List to a Role definition.  If the provided Role definition does not already exist, then the code will create a new Role definition and assign the Permission List to it.

import %metadata:Key;
import %metadata:RoleDefn:RoleDefn_Manager;
import %metadata:RoleDefn:RoleDefn;
import %metadata:RoleDefn:Roleclass;

Local %metadata:RoleDefn:RoleDefn &oRoleDefn;
Local %metadata:RoleDefn:Roleclass &oRoleclass;
Local boolean &bSaved;
Local string &sRoleName = "PSM_TEMP1";
Local string &sDescr = "My Descr";
Local string &DescrLong = "My Long Descr";
Local string &sRoleClass = "PTPT1000";

/* Instantiate the Key object */
Local %metadata:Key &oKey = create %metadata:Key();

/* Add the Role Name key */
&oKey.AddItem(Key:Class_RoleName, &sRoleName);

/* Instantiate the Role Defn Manager object */
Local %metadata:RoleDefn:RoleDefn_Manager &oManager = create %metadata:RoleDefn:RoleDefn_Manager();

/* Determine if a Role Definition exists for the given key */
Local boolean &bExists = &oManager.DefnExists(&oKey);

If &bExists Then
 &oRoleDefn = &oManager.GetDefn(&oKey);
 Local integer &i;
 /* Check if the Permission List is already assigned to the Role */
  For &i = 1 To &oRoleDefn.Count_Roleclass
   If (&oRoleDefn.Get_Roleclass(&i).Classid = &sRoleClass) Then
    MessageBox(0, "", 0, 0, "Permission List " | &sRoleClass | " is already assigned to Role " | &sRoleName);
    Return
   End-If;
 End-For;
 
 MessageBox(0, "", 0, 0, "Assigning Permission List " | &sRoleClass | " to existing Role " | &sRoleName);
 /* Get updatable Role definition and assign the Permission List to it */
 &oRoleDefn = &oManager.GetDefnToUpdate(&oKey);
 &oRoleclass = &oRoleDefn.Append_Roleclass(&oRoleDefn.Count_Roleclass);
 &oRoleclass.Classid = &sRoleClass;
 &bSaved = &oRoleDefn.UpdateDefn();
Else
 MessageBox(0, "", 0, 0, "Creating new Role " | &sRoleName | " and assigning Permission List " | &sRoleClass);
 /* Create new Role definition and assign the Permission List to it */
 &oRoleDefn = &oManager.CreateDefn();
 &oRoleDefn.Rolename = &sRoleName;
 &oRoleDefn.Descr = &sDescr;
 &oRoleDefn.Descrlong = &DescrLong;
 &oRoleclass = &oRoleDefn.Append_Roleclass(&oRoleDefn.Count_Roleclass);
 &oRoleclass.Classid = &sRoleClass;
 &bSaved = &oRoleDefn.SaveNewDefn();
End-If;

If Not &bSaved Then
 throw CreateException(0, 0, "Error saving Role definition");
End-If;

While I did not provide concrete examples of how to manipulate every possible object type with %metadata, I hope that I shined enough light on the subject to provide direction on how to go about manipulating any particular object type.  I believe that after gaining an understanding of the major aspects (Definitions, Managers, and Keys) of %metadata, one can fairly easily stumble their way through the usage of this package.

I think there can be many interesting use cases of the %metadata application package. I am personally putting this package to use by building out a PIA-based (online) PeopleSoft IDE. At the moment my online IDE is just a PeopleCode event editor with a horrible UI, but it is worth mentioning that I have had great success so far with using this package to view/update PeopleCode on any of the PepleCode events.  My biggest hurdle at the moment is coming with a JavaScript-based PeopleCode syntax highlighter/parser.  Here is a post documenting the Online PeopleCode Editor Project.

20 thoughts on “Understanding the %metadata Application Package

  1. Jonathan Rehm says:

    Hey Colton, regarding syntax highlighting, perhaps you could use https://github.com/Jatz/PeopleCodeTools as an assist to create a syntax definitions for one of the existing JavaScript highlighters? I know very little about it, but it would provide a starting place without entirely re-inventing things.

    • Colton Fischer says:

      Hi Jonathan! That is a good point on making use of pre-made syntax definitions. When I was doing some initial research on this, I found a similar project for Notepad++. Hopefully I can make use of the syntax definitions in these projects.

      I am still a little unsure on which JavaScript highlighter I should make use of. It seems that the highlight.js library is pretty popular, but I am unsure at this point how easy it is to use.

  2. Andrew says:

    Hi there, did you discover these different classes through trial and error or some other method? There is a delivered page I saw once creating PeopleCode evens which I was able to replicate/use. But haven’t had much luck creating a record with fields.

    • Colton Fischer says:

      The class discovery was a trial and error process unfortunately… Creating records is tricky because a lot of the default record properties have to be set manually in the code. So if you do not set all of the properties before you save the record definition, then App Designer will complain when you open the definition.

  3. Neil Yetman says:

    Hi Colton. Re syntax highlighting, I was going to suggest you look at the highlight.js library too. I’ve had some experience coming up with PeopleCode syntax files in the past for extenssions for WikiMedia wikis I’ve worked with. The first one was for the Geshii highlighter. The second (just last autumn) was for the Pygments highlighter. I could possibly lend a hand if you’d like. Just let me know.

  4. Mark H says:

    This is an outstanding post. Best I’ve seen in awhile. I think I have figured out most key:Classes for obtaining Peoplecode but Application Engine is stumping me. I have Class_AEStep, Class_AESection and Class_Method but cannot figure out what the key for the actual AE program is. The top key. Have you ever uncovered that?

      • Mark H says:

        Ohhhh, i feel so dumb. I thought i had tried every combination i could think of. That’s it. Thanks again and carry on the great work!

      • Ashok Rangineni says:

        I am trying to read AppEngine Peoplecode by applying below key constrains but I am getting error ‘First operand of . is NULL, so cannot access member GetProgram. (180,236)’ while retrieving peoplecode string using GetProgram(). Is there anything i’m missing. Please help me out on this.

        &oKey.AddItem(Key:Class_AEApplicationId, “ZZ_VND_CV_AE”);
        &oKey.AddItem(Key:Class_AESection, “STLODPRS”);
        &oKey.AddItem(Key:Class_AEStep, “Step01”);
        &oKey.AddItem(Key:Class_Method, “OnExecute”);
        Local %metadata:PeopleCodeProgram:PeopleCodeProgram_Manager &oManager = create %metadata:PeopleCodeProgram:PeopleCodeProgram_Manager();
        Local %metadata:PeopleCodeProgram:PeopleCodeProgram &oPeopleCodeProgram = &oManager.GetDefn(&oKey);
        Local string &sProgram = &oPeopleCodeProgram.GetProgram();

        • Colton Fischer says:

          I am unfortunately not sure how to reference App Engine PeopleCode. I noticed in your code that you are not making use of the App Engine Action key (Key:Class_AEAction). This key, combined with the others, could be the proper key combination to reference App Engine PeopleCode, but I am not certain.

          If you figure out how to reference App Engine PeopleCode, then please share.

        • Mark H says:

          This is how I access AE code:

          &oKey.AddItem(Key:Class_AEApplicationId, PSPCMPROG.OBJECTVALUE1);
          &oKey.AddItem(Key:Class_AESection, PSPCMPROG.OBJECTVALUE2);
          &oKey.AddItem(Key:Class_Market, PSPCMPROG.OBJECTVALUE3);
          &oKey.AddItem(Key:Class_DBType, PSPCMPROG.OBJECTVALUE4);
          &oKey.AddItem(Key:Class_EffDt, PSPCMPROG.OBJECTVALUE5);
          &oKey.AddItem(Key:Class_AEStep, PSPCMPROG.OBJECTVALUE6);
          &oKey.AddItem(Key:Class_Method, PSPCMPROG.OBJECTVALUE7);

          • Gangaram says:

            @Mark H Thanks for sharing the info of accessing the AE. I am just curious to know how did you find these Class_AESection,Class_Market can be used to refer the object.

  5. Jason L says:

    Any chance anyone has been working on the roledefn builder? I have been building a lot of the objects in peoplecode and I am stumped on how to add the permission list to the role… I think I am really close as when I try to use the following code only the ” &rc = &oRoleDefn.Insert_Rct(1);” line fails…

    &oRoleDefn = &oRoleDefn_Manager.CreateDefn();
    &oRoleDefn.Rolename = &Rolename;
    &oRoleDefn.Descr = &descr;
    &oRoleDefn.Descrlong = &DESCRLONG;

    Local %metadata:RoleDefn:Roleclass &rc;
    &rc = &oRoleDefn.Insert_Rct(1);

    &rc.Classid = &CLASSID;
    If Not (&oRoleDefn.SaveNewDefn()) Then

    End-If;

    • Colton Fischer says:

      Hi Jason. I think Role definition management is a great use case for the %metadata package. I just updated the post with an example of how to assign a Permission List to a Role definition using the %metadata package. Please see the “Another Complete %metadata Example” section above. Let me know if you have any other questions.

      • Jason L says:

        Thank you very much, I will implement it right away. I am very curious how you figured that out so quickly? I have found ways to get most objects but there always seems to be a few things that I can not seem to figure out. I thought your section on “Understanding %metadata Keys” would be what I have been looking for but for some reason I am not getting the same error message you are which would tell me what methods I am missing. Again Thank you very much

        • Colton Fischer says:

          I am not aware of a way for App Designer to tell me which methods exist for a %metadata class. I am only aware of how to get App Designer tell me which methods don’t exist. This is done by guessing a method name, saving the code, and see if App Designer barks. It is a painful process, but the “method name guessing game” becomes easier the more you work with the %metadata package. I find that methods are similarly named among the different %metadata classes.

          The error message in the “Understanding %metadata Keys” section is a trick that I use to determine the %metadata method parameter types. The trick is to extend the given %metadata class methods and then App Designer will tell you if you supply the incorrect input parameter types for the methods. However, you still have to know the method names for this to be of use.

Leave a Reply

Your email address will not be published.