ComponentInterfaceShell/Usage

Component Interface Shell Usage

Starting

The most straightforward way to start the CI shell is with a batch file/shell script similar to the following. This particular example is for Windows, but Unix/Linux are also supported; you'll just want to make the appropriate changes for your environment.

C:\>%PS_HOME%\jre\bin\java -jar c:\cishell\jython.jar -i c:\cishell\ci_wrap.py -i

This assumes that

  • Your PS_HOME environment variable has been set properly.
  • Your PS_HOME has Java installed in it. If not, you just need to supply a correct path to java or have java in your PATH.
  • You downloaded the main CI Shell file, ci_wrap.py, into c:\cishell. If not, supply the correct path to where you put it.
  • You copied the jython.jar file into c:\cishell. If not, supply the correct path to where you put it.

What this does is to start Jython and tell it to automatically load the code in the ci_wrap.py file (which is what implements the ComponentInterfaceShell). The two -i flags turn on the interactive mode, which will leave you at the interactive command prompt for the ComponentInterfaceShell. When you are working in interactive mode, component interfaces are automatically set to be in interactive mode as well. See below for the appropriate command line for running ComponentInterfaceShell scripts that are not interactive.

Logging In

After you are at the shell command prompt, you'll want to create a session with an application server. These initial examples focus on connecting to a single PeopleSoft instance. See below for how to connect to multiple PeopleSoft instances at once.

>>> sess = ComponentInterfaceSession('localhost:9000', 'VP1', 'VP1')
Interactive mode detected; CIs will automatically be set interactive
Connected to localhost:9000 as VP1 >>>

You can call your session variable whatever you want, we use sess here.

The first parameter is an application server connect string. The simplest connect string is the server name or IP address and the port number of the Jolt listener (separated by a colon). You can pass any valid Tuxedo connect string though. Here's an example with multiple application servers.

>>> sess = ComponentInterfaceSession('other.server.com:9010,localhost:9000', 'VP1', 'VP1')
Interactive mode detected; CIs will automatically be set interactive
Connected to other.server.com:9010,localhost:9000 as VP1 >>>

The next two parameters are the user ID and password. If you are working with a demo environment where the passwords are the same as the user IDs you don't need to supply the password at all; it will just default as the user ID. Currently the CI Shell only supports user/password authentication, although let us know if you're interested in other authentication types as  we know a thing or two about PeopleSoft authentication.

>>> sess = ComponentInterfaceSession('localhost:9000', 'VP1')
Interactive mode detected; CIs will automatically be set interactive
Connected to localhost:9000 as VP1 >>>

Loading a Component Interface

Once you have a session established, the prompt will change to reflect that. You can use the getCompIntfc method to obtain a component interface object. You can then user the create method for adding new data via the component interface, or the get method to load existing data.

Connected to localhost:9000 as VP1 >>> ci = sess.getCompIntfc('COUNTRY')
Connected to localhost:9000 as VP1 >>> ci.COUNTRY='USA'
Connected to localhost:9000 as VP1 >>> ci.get()
1
Connected to localhost:9000 as VP1 >>> ci
COUNTRY (COUNTRY=USA)

Viewing Data

You can access specific fields by referencing the field name as defined in the component interface. This is almost always the same as the underlying field name (although there are exceptions).

Connected to localhost:9000 as VP1 >>> ci.COUNTRY
USA
Connected to localhost:9000 as VP1 >>> ci.DESCR
United States
Connected to localhost:9000 as VP1 >>> ci.COUNTRY_2CHAR
US

You can view all of the data/attributes of a component interface by calling the dump() method on an instantiated component interface object.

Connected to localhost:9000 as VP1 >>> ci.dump()
- COUNTRY=USA
- DESCR=United States
- DESCRSHORT=USA
- COUNTRY_2CHAR=US
- EU_MEMBER_STATE=N
- ADDR1_LBL=Address 1
- ADDR1_AVAIL=Y
- ADDR2_LBL=Address 2
- ADDR2_AVAIL=Y
- ADDR3_LBL=Address 3
- ADDR3_AVAIL=Y
- ADDR4_LBL=
- ADDR4_AVAIL=N
- CITY_LBL=City
- CITY_AVAIL=Y
- NUM1_LBL=
- NUM1_AVAIL=N
- NUM2_LBL=
- NUM2_AVAIL=N
- HOUSE_TYPE_LBL=
- HOUSE_TYPE_AVAIL=N
- ADDR_FIELD1_LBL=
- ADDR_FIELD1_AVAIL=N
- ADDR_FIELD2_LBL=
- ADDR_FIELD2_AVAIL=N
- ADDR_FIELD3_LBL=
- ADDR_FIELD3_AVAIL=N
- COUNTY_LBL=County
- COUNTY_AVAIL=Y
- STATE_LBL=State
- STATE_AVAIL=Y
- POSTAL_LBL=Postal:
- POSTAL_AVAIL=Y
- IN_CITY_LIM_AVAIL=N
- IN_CITY_LIM_LBL=In City Limi
- GEO_CODE_AVAIL=N
- GEO_CODE_LBL=Geo Cod
- POST_SRCH_AVAIL=N
- GBSYS_NRPATH_UK=
- GBSYS_CFGPATH_UK=
- interactiveMode=1
- getHistoryItems=0
- editHistoryItems=0
- componentName=COUNTRY
- compIntfcName=COUNTRY
- Market=GBL
- description=None
- getDummyRows=1
- stopOnFirstError=0
- propertyInfoCollection=PropertyInterfaceCollection (40 rows)
- createKeyInfoCollection=PropertyInterfaceCollection (1 rows)
- getKeyInfoCollection=PropertyInterfaceCollection (1 rows)
- findKeyInfoCollection=PropertyInterfaceCollection (2 rows)
Connected to localhost:9000 as VP1 >>>

You can dump just a subset of the component's data as well. Here we dump out just the 7th role that VP1 has in this environment. Note that we reference the 7th row with the number 6 since jython/python/java are all 0-based, not 1-based, for sequence data like arrays and lists.

>>> ci = sess.getCompIntfc('USER_PROFILE')
>>> ci.get(UserID='VP1')
>>> ci.Roles[6].dump()
- itemNum=6
- RoleName=ProcessSchedulerAdmin
- Dynamic=N
- RouteControls row 0
-- itemNum=0
-- RouteControlProfile=
>>>

Saving Changes

You can save your changes by making updates to the component interface and then invoking the save() method.

Connected to localhost:9000 as VP1 >>> ci.ADDR1_LBL
'Address 1'
Connected to localhost:9000 as VP1 >>> ci.ADDR1_LBL = 'My House'
Connected to localhost:9000 as VP1 >>> ci.save()
1
Connected to localhost:9000 as VP1 >>> ci.get(COUNTRY='USA')
1
Connected to localhost:9000 as VP1 >>> ci.ADDR1_LBL
'My House'
Connected to localhost:9000 as VP1 >>>

Creating Standalone Scripts

The commands that you type in the ComponentInterfaceShell are just standard python code, so they can easily be saved off as standalone files. The merit_raises.py file is a standalone script that uses the CI_JOB_DATA component interface to grant annual merit increases to a set of employees.

In an PeopleSoft HCM 9.1 environment, we installed the ComponentInterfaceShell as per the installation notes and then ran the following command at a command prompt.

C:\cishell>%PS_HOME%\jre\bin\java -jar c:\cishell\jython.jar c:\cishell\merit_raises.py

Here's the script output from the HCM 9.1 demo database. It looks at employee IDs starting with K0H and gives a merit increase to anyone with hourly pay components. It prints the updated salary, or prints out an error messages about the raise couldn't be granted.

CI_JOB_DATA (KEYPROP_EMPLID=K0HU10,KEYPROP_EMPL_RCD=0)
Pay changed from 3336.666667 to 3670.333333
CI_JOB_DATA (KEYPROP_EMPLID=K0HU10,KEYPROP_EMPL_RCD=1)
Pay changed from 3432.000000 to 3775.200000
CI_JOB_DATA (KEYPROP_EMPLID=K0HU11,KEYPROP_EMPL_RCD=0)
Pay changed from 8246.333333 to 9070.966667
CI_JOB_DATA (KEYPROP_EMPLID=K0HU12,KEYPROP_EMPL_RCD=0)
Pay changed from 6959.333333 to 7655.266667
CI_JOB_DATA (KEYPROP_EMPLID=K0HU13,KEYPROP_EMPL_RCD=0)
Pay changed from 7007.000000 to 7707.700000
CI_JOB_DATA (KEYPROP_EMPLID=K0HU14,KEYPROP_EMPL_RCD=0)
Pay changed from 6673.333333 to 7340.666667
CI_JOB_DATA (KEYPROP_EMPLID=K0HU15,KEYPROP_EMPL_RCD=0)
Pay changed from 6673.333333 to 7340.666667
CI_JOB_DATA (KEYPROP_EMPLID=K0HU16,KEYPROP_EMPL_RCD=0)
Pay changed from 5720.000000 to 6292.000000
CI_JOB_DATA (KEYPROP_EMPLID=K0HU17,KEYPROP_EMPL_RCD=0)
Pay changed from 4004.000000 to 4404.400000
CI_JOB_DATA (KEYPROP_EMPLID=K0HU18,KEYPROP_EMPL_RCD=0)
Warning -- Benefit System not unique for current jobs with same Benefit Record Number. (3000,557)
Pay changed from 1078.000000 to 1185.800000
CI_JOB_DATA (KEYPROP_EMPLID=K0HU18,KEYPROP_EMPL_RCD=1)
Warning -- Benefit System not unique for current jobs with same Benefit Record Number. (3000,557)
Pay changed from 616.000000 to 677.600000
CI_JOB_DATA (KEYPROP_EMPLID=K0HU19,KEYPROP_EMPL_RCD=0)
Pay changed from 2464.000000 to 2710.400000
CI_JOB_DATA (KEYPROP_EMPLID=K0HU20,KEYPROP_EMPL_RCD=0)
Warning -- Hourly Rate is greater than the maximum specified in the Salary Grade Table. (1000,33)
Pay changed from 1584.000000 to 1742.400000
CI_JOB_DATA (KEYPROP_EMPLID=K0HU21,KEYPROP_EMPL_RCD=0)
Pay changed from 2156.000000 to 2371.600000
CI_JOB_DATA (KEYPROP_EMPLID=K0HU22,KEYPROP_EMPL_RCD=0)
Pay changed from 616.000000 to 677.600000
CI_JOB_DATA (KEYPROP_EMPLID=K0HU23,KEYPROP_EMPL_RCD=0)
Pay changed from 2464.000000 to 2710.400000
CI_JOB_DATA (KEYPROP_EMPLID=K0HU24,KEYPROP_EMPL_RCD=0)
Pay changed from 704.000000 to 774.400000
CI_JOB_DATA (KEYPROP_EMPLID=K0HU25,KEYPROP_EMPL_RCD=0)
Warning -- Hourly Rate is greater than the maximum specified in the Salary Grade Table. (1000,33)
Pay changed from 880.000000 to 968.000000

Tips

Passing Name/Value pairs

The underlying Component Interface API that PeopleSoft delivers normally requires that you set each value one by one. If you set 10 properties, then you have 10 lines of code. The ComponentInterfaceShell library provides some syntactic sugar to make this a bit easier though. The methods create, find, get, insert, and save all accept name/value pairs and applies them automatically for you.

For example, when you call get to instantiate a component interface, you can pass the keys that the component interface needs directly to the get method, instead of setting them one by one.

>>> ci = sess.getCompIntfc('PROD_DEFN_CI')
>>> ci.get(SETID='SHARE', PRODUCT_ID='10000')
1

vs

>>> ci = sess.getCompIntfc('PROD_DEFN_CI')
>>> ci.get(SETID='SHARE', PRODUCT_ID='10000')
>>> ci.SETID='SHARE'
>>> ci.PRODUCT_ID='10000'
>>> ci.get()
1

This also works for updating any values at level 0 in a component when you are going to call save(). The following example will set the AccountLocked property to 1 (locking the account) and the CurrencyCode property to the Australian Dollar (AUD), and then invoke save on the underlying component interface.

>>> ci = sess.getCompIntfc('USER_PROFILE')
>>> ci.get(UserID='VP1')
1
>>> ci.save(AccountLocked='1',CurrencyCode='AUD')
1
>>>

When inserting new rows of data at levels 1, 2, or 3, you can do similar things with the insert method.

job_data_ci = sess.getCompIntfc('CI_JOB_DATA')
emplid = 'K0HU16'
job_rec_nbr = '0'
if job_data_ci.get(KEYPROP_EMPLID=emplid, KEYPROP_EMPL_RCD=job_rec_nbr):
    # creates a new row with ACTION='PAY' and ACTION_REASON='MER'
    merit_row = job_data_ci.COLL_JOB.insert(PROP_ACTION='PAY', PROP_ACTION_REASON='MER')

    # perform merit increase calculations or read new value from file, etc. here

    merit_row.PROP_CALC_COMP_BTN = 'Y'     # trigger the PeopleCode calculation logic
    
    if not job_data_ci.save():
        for msg in sess.messages():
            print msg

If you have a number of attributes to update, but need to do it at a different point than getting, saving, inserting, etc. you can use the update method, which accepts name/value pairs and applies them. The update method works on all levels (0, 1, 2, 3) of a component interface.

Viewing Messages

If messages are issued during component processing, you can call the messages method on the session object. Here, we try to assign a too large value to a field, which triggers an error message (but one that does not help solve the underlying problem), so we check the messages issued.

Connected to localhost:9000 as VP1 >>> ci.COUNTRY_2CHAR = 'BLAH'
Traceback (innermost last):
  File "<console>", line 1, in ?
  File "z:\work\cgs\projects\componentinterfaceshell\trunk\ci_wrap.py", line 191
, in __setattr__
        at psft.pt8.joa.CISvc.setProperties(CISvc.java:94)
        at psft.pt8.joa.CI.flush(CI.java:367)
        at psft.pt8.joa.CI.addQueuedOp(CI.java:385)
        at psft.pt8.joa.CI.setProperty(CI.java:178)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)

psft.pt8.joa.JOAException: psft.pt8.joa.JOAException: TPESVCFAIL - application l
evel service failure
Connected to localhost:9000 as VP1 >>> sess.messages()
['The value BLAH is too long for the field COUNTRY_TBL.COUNTRY_2CHAR (91,68)', 
'Failed to execute PSBusComp request  ']
Connected to localhost:9000 as VP1 >>>

Calling messages() automatically clears the messages from the session. You can call messages(clear=False) if you don't want the messages automatically cleared. You can also test if there are any errors or warnings pending without displaying or clearing the messages by calling pending() on the session object.

Meta-data

You can review meta-data about the component interface by accessing any of the following attributes

  • createKeyInfoCollection
  • findKeyInfoCollection
  • getKeyInfoCollection
  • propertyInfoCollection
Connected to localhost:9000 as VP1 >>> ci.findKeyInfoCollection
PropertyInterfaceCollection (2 rows)
COUNTRY(COUNTRY_TBL.COUNTRY),required,label=Cntry(Country)
DESCR(COUNTRY_TBL.DESCR),required,label=Descr(Description)
Connected to localhost:9000 as VP1 >>> ci.propertyInfoCollection
PropertyInterfaceCollection (40 rows)
COUNTRY(COUNTRY_TBL.COUNTRY),required,label=Cntry(Country)
DESCR(COUNTRY_TBL.DESCR),required,label=Descr(Description)
DESCRSHORT(COUNTRY_TBL.DESCRSHORT),label=Short Desc(Short Description)
COUNTRY_2CHAR(COUNTRY_TBL.COUNTRY_2CHAR),label=2-Char Cd(2-Char Country Code)
EU_MEMBER_STATE(COUNTRY_TBL.EU_MEMBER_STATE),label=EU(EU Member State)
ADDR1_LBL(COUNTRY_TBL.ADDR1_LBL),label=Address 1:(Address 1 Label)
ADDR1_AVAIL(COUNTRY_TBL.ADDR1_AVAIL),required,label=Available(Address 1 Available)
ADDR2_LBL(COUNTRY_TBL.ADDR2_LBL),label=Address 2(Address 2 Label)
ADDR2_AVAIL(COUNTRY_TBL.ADDR2_AVAIL),label=Available(Address 2 Available)
ADDR3_LBL(COUNTRY_TBL.ADDR3_LBL),label=Address 3(Address 3 Label)
ADDR3_AVAIL(COUNTRY_TBL.ADDR3_AVAIL),label=Available(Address 3 Available)
ADDR4_LBL(COUNTRY_TBL.ADDR4_LBL),label=Address 4(Address 4 Label)
ADDR4_AVAIL(COUNTRY_TBL.ADDR4_AVAIL),label=Available(Address 4 Available)
CITY_LBL(COUNTRY_TBL.CITY_LBL),label=City(City Label)
CITY_AVAIL(COUNTRY_TBL.CITY_AVAIL),label=Available(City Available)
NUM1_LBL(COUNTRY_TBL.NUM1_LBL),label=Nbr 1(Number 1 Label)
NUM1_AVAIL(COUNTRY_TBL.NUM1_AVAIL),label=Available(Number 1 Available)
NUM2_LBL(COUNTRY_TBL.NUM2_LBL),label=Nbr 2(Number 2 Label)
NUM2_AVAIL(COUNTRY_TBL.NUM2_AVAIL),label=Available(Number 2 Available)
HOUSE_TYPE_LBL(COUNTRY_TBL.HOUSE_TYPE_LBL),label=House Label(House Type Label)
HOUSE_TYPE_AVAIL(COUNTRY_TBL.HOUSE_TYPE_AVAIL),label=Available(House Type Available)
ADDR_FIELD1_LBL(COUNTRY_TBL.ADDR_FIELD1_LBL),label=Field 1 Label(Address Field 1 Label)
ADDR_FIELD1_AVAIL(COUNTRY_TBL.ADDR_FIELD1_AVAIL),label=Field 1 Avail(Address Field 1 Available)
ADDR_FIELD2_LBL(COUNTRY_TBL.ADDR_FIELD2_LBL),label=Field 2 Label(Address Field 2 Label)
ADDR_FIELD2_AVAIL(COUNTRY_TBL.ADDR_FIELD2_AVAIL),label=Field 2 Avail(Address Field 2 Available)
ADDR_FIELD3_LBL(COUNTRY_TBL.ADDR_FIELD3_LBL),label=Field 3 Label(Address Field 3 Label)
ADDR_FIELD3_AVAIL(COUNTRY_TBL.ADDR_FIELD3_AVAIL),label=Field 3 Avail(Address Field 3 Available)
COUNTY_LBL(COUNTRY_TBL.COUNTY_LBL),label=County(County Label)
COUNTY_AVAIL(COUNTRY_TBL.COUNTY_AVAIL),label=Available(County Available)
STATE_LBL(COUNTRY_TBL.STATE_LBL),label=Label(State Label)
STATE_AVAIL(COUNTRY_TBL.STATE_AVAIL),label=Available(State Available)
POSTAL_LBL(COUNTRY_TBL.POSTAL_LBL),label=Postal(Postal Label)
POSTAL_AVAIL(COUNTRY_TBL.POSTAL_AVAIL),required,label=Available(Postal Available)
IN_CITY_LIM_AVAIL(COUNTRY_TBL.IN_CITY_LIM_AVAIL),label=In City Limit
IN_CITY_LIM_LBL(COUNTRY_TBL.IN_CITY_LIM_LBL),label=In City Limit
GEO_CODE_AVAIL(COUNTRY_TBL.GEO_CODE_AVAIL),label=Geo Cd Avail(Geo Code Available)
GEO_CODE_LBL(COUNTRY_TBL.GEO_CODE_LBL),label=Geo Code Label(Geographic Code Label)
POST_SRCH_AVAIL(COUNTRY_TBL.POST_SRCH_AVAIL),label=Postal  Search(Post Search Available)
GBSYS_NRPATH_UK(COUNTRY_TBL.GBSYS_NRPATH_UK),label=NR Directory(National Registry Directory)
GBSYS_CFGPATH_UK(COUNTRY_TBL.GBSYS_CFGPATH_UK),label=Config Prefix(Configuration File Prefix)
Connected to localhost:9000 as VP1 >>> 

Connecting to Multiple PeopleSoft Systems

The examples above have all just connected to a single PeopleSoft environment by creating one ComponentInterfaceSession object. However, there is no reason that you can't connect to multiple PeopleSoft environments at once by creating additional ComponentInterfaceSession objects.

hcm_sess  = ComponentInterfaceSession('hcm.server:9000', 'PS', 'PS')
fscm_sess = ComponentInterfaceSession('fscm.server:9100', 'VP1', 'VP1')

You can use each of the session objects that were created, including referencing values between the environments. Here's a snippet from the multi_system_example.py demo script where we have connected to an HCM 9.1 environment on PeopleTools 8.50 and a CRM 9.0 environment on PeopleTools 8.48. This code loops through the COUNTRY component interface in HCM and checks whether or not the same country definition exists in CRM.

for hcm_country in hcm_ci.find():
    hcm_country.get()
    if not crm_ci.get(COUNTRY=hcm_country.COUNTRY):
        print 'HCM country %s(%s) does not exists in CRM' % (hcm_country.COUNTRY, 
                                                             hcm_country.DESCR)
HCM country ALA(Aland Islands) does not exists in CRM
HCM country BLM(Saint Barthelemy) does not exists in CRM
HCM country GGY(Guernsey) does not exists in CRM
HCM country IMN(Isle of Man) does not exists in CRM
HCM country JEY(Jersey) does not exists in CRM
HCM country KOS(Kosovo) does not exists in CRM
HCM country MAF(Saint Martin) does not exists in CRM
HCM country MNE(Republic of Montenegro) does not exists in CRM
HCM country PSE(Palestinian Territory, Occupie) does not exists in CRM
HCM country ROU(Romania) does not exists in CRM
HCM country SRB(Republic of Serbia) does not exists in CRM
HCM country TLS(East Timor) does not exists in CRM

If you are connecting to PeopleSoft systems that are on different PeopleTools versions, then you need to provide the path to the correct version of the psjoa.jar file when creating your ComponentInterfaceSession object because the psjoa.jar files check their version against the version of the environment that they connect to. The path should be specific to your local file system where you are running the ComponentInterfaceShell.

hcm_sess  = ComponentInterfaceSession('hcm.server:9000', 'PS', 'PS')
fscm_sess = ComponentInterfaceSession('fscm.server:9100', 'VP1', 'VP1', psjoa='/path/to/psjoa.jar')

If you try to use the wrong version of psjoa.jar for the PeopleSoft environment that you are connecting to, you will see errors like the following in the PeopleSoft application server log files. These errors are from using the psjoa.jar file for PeopleTools 8.50 and trying to connect to an environment that is on PeopleTools 8.48.

PSAPPSRV.1236 (8) [08/23/10 10:36:19 GetCertificate](1) (NET.503): PeopleTools release (8.50) for web server '' is not the same as Application Server PeopleTools release (8.48).  Access denied.  
PSAPPSRV.1236 (8) [08/23/10 10:36:19 GetCertificate](1) (NET.346): Failed to execute GetCertificate request  

The psjoa.jar files are standalone though; they have no other PeopleTools dependencies, so you do not need to have access to the entire PS_HOME for each environment; you only need the psjoa.jar file for the applicable PeopleTools version.

Note that you can actually pass the psjoa parameter for the first connection as well. The ComponentInterfaceShell code falls back to using the PS_HOME environment variable only if the psjoa parameter is not supplied for creating the ComponentInterfaceSession.