A question came up today that I have wondered about for a while: how does break and evaluate work in PeopleCode? I think I have been telling it wrong, so I decided to do a little experiment.
Note: This test uses the Unit Test Framework.
A question came up today that I have wondered about for a while: how does break and evaluate work in PeopleCode? I think I have been telling it wrong, so I decided to do a little experiment.
Note: This test uses the Unit Test Framework.
I had a need to Ping a server to see if the server could get to it. I also tried to post to it. This code could be helpful for others, so I want to share it. A post should normally go through the Integration Broker, but I first developed it at a time when I had a product that was supposed to go on servers with diverse versions of Integration Broker.
The Ping code doesn’t seem very reliable for some reason. Something on the Java side doesn’t always work. Still it might be helpful.
Local JavaObject &url; Local JavaObject &conn; Local JavaObject &r; Local any &line; Local string &output; Local JavaObject &inet; Local string &address; MessageBox(0, "", 0, 0, "hostname: " | GetJavaClass("java.net.InetAddress").getLocalHost().getHostName()); &address = "localhost"; &inet = GetJavaClass("java.net.InetAddress").getByName(&address); MessageBox(0, "", 0, 0, &address | "(" | &inet.getHostAddress() | ") reachable: " | &inet.isReachable(5000)); &address = "www.google.com"; &inet = GetJavaClass("java.net.InetAddress").getByName(&address); MessageBox(0, "", 0, 0, &address | "(" | &inet.getHostAddress() | ") reachable: " | &inet.isReachable(5000)); &url = CreateJavaObject("java.net.URL", "http://www.google.com"); &conn = &url.openConnection(); &conn.setRequestProperty("content-type", "application/x-www-form-urlencoded"); &conn.setRequestProperty("accept", "text/xml/html"); &conn.setRequestProperty("accept-charset", "utf-8, iso_8859-1"); &conn.setRequestProperty("userid", "????"); &conn.setRequestProperty("pwd", "?????"); &conn.setDoOutput( True); &output = ""; &r = CreateJavaObject("java.io.BufferedReader", CreateJavaObject("java.io.InputStreamReader", &conn.getInputStream())); &line = &r.readLine(); While &line <> Null &output = &output | Char(10) | Char(13) | &output; &line = &r.readLine(); End-While; &r.close(); MessageBox(0, "", 0, 0, &output);
Please make sure to adjust the URLs and hostnames to what you need.
One of my old tricks was to create a PeopleCode dump from the system. Then I could use a text editor tool such as gVim or Ultraedit to search through the code to find examples or certain uses of definitions.
Basically, a PeopleCode dump is easily created by searching for (Edit > Find In) a semicolon. Because every statement must have a semicolon, it matches every statement of code in the system. On the Find In dialog, checking the Save to File option will write out each line to a text file.
The problem is that Application Designer seems to load every line into memory. If you don’t have a client machine with a ton of memory, you end up looking like this:
Instead, I found this new tool called Decode PeopleCode. You can download the latest zipped version of the program from here.
Once extracted, I had to configured the URL property in the DecodePC.properties file. I set it to something like this:
url=jdbc:oracle:thin:@<server name>:1521/<db name>
I also had a problem with the JDBC driver for some reason. It didn’t find the odbc5.jar file that was delivered. I already had a copy of the odbc6.jar, so I just adjusted the classpath to include that. If you run into the same problem, adjust the DecodePCODE.sh or DecodePCODE.bat file to contain the full path to the jar file.
Also, adjust the “outdir” property in the DecodePC.properties file to point to the directory where you want the output dumped. Note that it will create a bunch of folders and files in that directory, so you may want to point it to an empty directory.
Then, I ran the program with this command to capture everything:
sh DecodePCODE.sh since 1901/01/01
Now, that creates each PeopleCode program in its own file. The problem is that I want it all in a single file that I can run regular expressions against. I could use grep, but I am used to a single file. I cobbled this script together to build that single file:
#!/bin/bash processFile() { outdirlen=$((${#OUTDIR} + 1)) filename=$1 lastUpdateFile=$( echo "$filename" | sed 's/\.pcode$/.last_update/' ) peoplecodePath=${filename:$outdirlen} peoplecodePath=$( echo "$peoplecodePath" | sed 's/\//: /' | sed 's/\.pcode$//' | sed 's/\//./g') echo -e "\e[0K\r Writing: $peoplecodePath" echo "[$peoplecodePath]" >> "$OUTFILE" echo '/* Details:' >> "$OUTFILE" echo ' Last Update User: ' $(head -n 1 "$lastUpdateFile") >> "$OUTFILE" echo ' Last Update Date/Time: ' $(tail -n 1 "$lastUpdateFile") >> "$OUTFILE" echo '*/' >> "$OUTFILE" cat "$filename" >> "$OUTFILE" } # ------------------------------ # Main # ------------------------------ OUTDIR=$( grep "outdir=" DecodePC.properties | sed 's/^outdir=//' | sed 's/\r//') DUMPDATE=$(stat -c %y $(ls -rt | tail -n 1) | cut -d ' ' -f1 ) DATABASE=$( grep "^url=" DecodePC.properties | sed 's/^url=.*\///' | sed 's/\r//') OUTFILE="$OUTDIR/all.pcode" echo "outdir -- $OUTDIR dump date -- $DUMPDATE database -- $DATABASE" echo "PeopleCode Dump $DATABASE $DUMPDATE" > "$OUTFILE" find "$OUTDIR" -type f -name \*.pcode | while read file; do processFile "$file"; done
That seemed to get the trick done for me nicely.
This program has more features than just this. You should explore it. It can check the code into a sourcecode repository to track changes. Kudos to whoever wrote it!
Just the other day, I needed to set a Date to null or blank, and I couldn’t remember how to do it. Here’s my notes so that I can remember next time. Javier’s blog came to the rescue:
Javier’s PeopleSoft blog: Setting Date Variables to Null in PeopleCode
The short version is: use the Date(0) function:
Local Date &hireDate; &hireDate = Date(0);
Here’s some more information to explore a little more in depth…
The process scheduler provides a good standard way to launch a process. You simply add a subpage to your run control page, and the delivered “Run” button does all of the work for you. But sometimes, you want to run the process other ways. Sometimes, you might want to create a more customized feel on a end-user page and launch a process from a push button. Or, you might want to launch an additional process from an App Engine program.
In this post, I would like to drop notes to make this easier the next time I need to do it.
I come upon a requirement to unzip a file in a platform independent way. Jim Marion got me most of the way, but his code didn’t write it to file. Here’s my adjustment to make a function that wrote it to file:
Function unzip(&inputZipFile, &targetDir) Local JavaObject &zipFileInputStream = CreateJavaObject("java.io.FileInputStream", &inputZipFile); Local JavaObject &zipInputStream = CreateJavaObject("java.util.zip.ZipInputStream", &zipFileInputStream); Local JavaObject &zipEntry = &zipInputStream.getNextEntry(); Local JavaObject &buf = CreateJavaArray("byte[]", 1024); Local number &byteCount; While &zipEntry <> Null If (&zipEntry.isDirectory()) Then REM ** do nothing; Else Local JavaObject &outFile = CreateJavaObject("java.io.File", &targetDir | &zipEntry.getName()); &outFile.getParentFile().mkdirs(); Local JavaObject &out = CreateJavaObject("java.io.FileOutputStream", &outFile); &byteCount = &zipInputStream.read(&buf); While &byteCount > 0 &out.write(&buf, 0, &byteCount); &byteCount = &zipInputStream.read(&buf); End-While; &zipInputStream.closeEntry(); End-If; &zipEntry = &zipInputStream.getNextEntry(); End-While; &zipInputStream.close(); &zipFileInputStream.close(); End-Function; unzip("/tmp/myzipfile.zip", "/tmp/out");
This is a continuation of the following posts:
This time, we are trying some of the suggestions from Dan on this comment:
So, let’s see what happens
This is a continuation of the following posts:
Now, we need to write some code to use the Component Interface. Let’s use the Application Engine program from this step by step post.
This is part one of a multi-part series exploring some quirks in using Component Interfaces with Application Engine programs. If nothing else, hopefully, these will give new developers some insight into how to use a Component Interface. My goal is to expose a bug in the Application Engine tool that maybe Oracle will see and fix.
This first part will simply walk you through creating a Component Interface. This part is just a map to associate the fields on the screen (or really in the component’s buffer) with an API property that can be accessed with code.
First, we create a new definition in Application Designer. You can either use the Ctrl + N keyboard shortcut or the File > New menu. Choose Component Interface from the list:
Next, have no fear — you will see the open dialog making it look like you want to open a component. Really, Application Designer is just asking you which component you want to map. In this example, we will use the “PERSONAL_DATA” component, which is the Modify a Person screen (Workforce Administration > Personal Information > Modify a Person):
Next, Application Designer asks you if you want to default the properties. I almost always say yes to this questions because it will make Application Designer do all the work for you in generating the map. The properties will be given names based on their field names in the buffer:
Now, you should have a new component interface generated for you. Notice that the left side is the Component Structure. It is the same as the Structure tab on the Component itself. The right side is the map of record/field to property name. In this screenshot, I have the component open in the background and I drew a line to show how the structure is the same. Then, I drew a line from the structure to the property generated for one of the fields:
Finally, save the component interface. You can either use the Ctrl + S keyboard shortcut, or you can use the File > Save menu. I gave it the name BLG_PERS_DTA_CI.
While your at it, you may also want to add it to the project. You can use the F7 keyboard shortcut or the Insert > Current Definition Into Project menu.
This concludes creating the Component Interface. Please stay tuned for the next steps …
This post is to address a question posed in one of the comments on another post. The answer is a little too in-depth for a comment reply. Also, I had been wanting to blog more about arrays anyway.
Here is the question:
Hi i have seen your comment on Arrays.
i need your guidence on creating array and using it in Peoplecode App package.
I have some logic written in SQR. Same thing i want create in App Package which can be used.
Let me brief you my requirement.
i have several parameters to be stored in an array which i will get from different validations and SQL tables for employees.which i will store it in a array. After finishing validations for all the employees i have pass those employee id and details some other system so i want to replicate in App package using Array can you guide me how i can design it in App package.
I also decided to use my step-by-step that I just completed. So, this answer will build on that post.
Listening to the requirements, I don’t think that we need to extend the array object like the post that this comment was on. At most, I think we might want to create an employee object that will store all of the fields (or parameters as the requirements call them) needed.
So, I am creating a new Application Package called BLG_BLOGGING. Then, I am inserting a new Application Class called “EmployeeObject”:
Then, I created three properties in that class to hold three parameters relating to an employee. I did not specify the “get” or “set” keywords so I don’t have to create getter and setter methods. This is the easiest way to add properties to a class because you don’t have to write any code for the properties. Here is the code:
class EmployeeObject property string EmployeeID; property string FirstName; property string LastName; end-class;
Then, in the Application Engine program, you have to import your new class. This statement does nothing more than tell the program you are going to use this class later on.
import BLG_BLOGGING:EmployeeObject;
Here are the different variables that we will need.
Local File &log; Local BLG_BLOGGING:EmployeeObject &emp; Local array of BLG_BLOGGING:EmployeeObject &ary; Local number &x;
This code creates an empty array. We need to pass it a copy of the employee object just so it knows what data type will be stored in the array. It will not actually store anything in the array at this point.
&emp = create BLG_BLOGGING:EmployeeObject(); &ary = CreateArrayRept(&emp, 0);
Here is where we load the first employee into the array. We set all of the properties, and then, we use the push() method to insert it into the array.
&emp.EmployeeID = "001"; &emp.FirstName = "Bob"; &emp.LastName = "Tomato"; &ary.Push(&emp); &log.WriteLine("Added Bob Tomato -- length = " | &ary.Len);
Then, we repeat that with another employee.
&emp = create BLG_BLOGGING:EmployeeObject(); &emp.EmployeeID = "002"; &emp.FirstName = "Larry"; &emp.LastName = "Cucumber"; &ary.Push(&emp); &log.WriteLine("Added Larry Cucumber -- length = " | &ary.Len);
Finally, we add a third employee just to give us some data.
&emp = create BLG_BLOGGING:EmployeeObject(); &emp.EmployeeID = "003"; &emp.FirstName = "Lunt"; &emp.LastName = "Squash"; &ary.Push(&emp); &log.WriteLine("Added Lunt Squash -- length = " | &ary.Len);
Then, we need to loop through the array to show what it contains. The Get() method accesses one of the elements in the array. It does not remove the element from the array.
&log.WriteLine(""); &log.WriteLine("Employees: "); For &x = 1 To &ary.Len &emp = &ary.Get(&x); &log.WriteLine(" " | &emp.EmployeeID | ") " | &emp.LastName | ", " | &emp.FirstName); End-For;
Here is the Application Package, Employee Object:
class EmployeeObject property string EmployeeID; property string FirstName; property string LastName; end-class;
Here is the App Engine Program:
import BLG_BLOGGING:EmployeeObject; Local File &log; Local BLG_BLOGGING:EmployeeObject &emp; Local array of BLG_BLOGGING:EmployeeObject &ary; Local number &x; &log = GetFile("c:\temp\log.txt", "W", "A", %FilePath_Absolute); &emp = create BLG_BLOGGING:EmployeeObject(); &ary = CreateArrayRept(&emp, 0); &emp.EmployeeID = "001"; &emp.FirstName = "Bob"; &emp.LastName = "Tomato"; &ary.Push(&emp); &log.WriteLine("Added Bob Tomato -- length = " | &ary.Len); &emp = create BLG_BLOGGING:EmployeeObject(); &emp.EmployeeID = "002"; &emp.FirstName = "Larry"; &emp.LastName = "Cucumber"; &ary.Push(&emp); &log.WriteLine("Added Larry Cucumber -- length = " | &ary.Len); &emp = create BLG_BLOGGING:EmployeeObject(); &emp.EmployeeID = "003"; &emp.FirstName = "Lunt"; &emp.LastName = "Squash"; &ary.Push(&emp); &log.WriteLine("Added Lunt Squash -- length = " | &ary.Len); &log.WriteLine(""); &log.WriteLine("Employees: "); For &x = 1 To &ary.Len &emp = &ary.Get(&x); &log.WriteLine(" " | &emp.EmployeeID | ") " | &emp.LastName | ", " | &emp.FirstName); End-For; &log.Close();
Here is the final output:
Added Bob Tomato -- length = 1 Added Larry Cucumber -- length = 2 Added Lunt Squash -- length = 3 Employees: 001) Tomato, Bob 002) Cucumber, Larry 003) Squash, Lunt