Jekyll2021-05-14T16:12:36+00:00http://peoplesoftmods.com/feed.xmlPeopleSoft ModsEnhancing PeopleSoft SystemsColton FischerDisplay iScripts in Fluid Nav Collections2020-08-05T00:56:55+00:002020-08-05T00:56:55+00:00http://peoplesoftmods.com/ux/display-iscripts-in-fluid-nav-collections<p>There is a pesky bug that has been a thorn in my side for quite some time. The issue is the inability to use iScript based Content References in Fluid Navigation Collections. Viewing an iScript CREF in a Fluid Nav Collection results in the left hand side navigation locking up. This is a <a href="https://support.oracle.com/epmos/faces/DocumentDisplay?id=2306226.1">documented bug</a> that does not have a solution listed.</p>
<h2 id="cause">Cause</h2>
<p>I found the cause of this issue to be the absence of the <code class="language-plaintext highlighter-rouge">bLoadCompleted</code> JavaScript variable in the iScript HTML response content. This variable appears in whats seems to be all of the HTML response content of Component based applications. Unlike Components, iScript response content is entirely controlled by the developer. This is great for the case of developing unaltered response content, but it is problematic in this scenario where the system navigation expects particular content to be present in the response.</p>
<h2 id="solution">Solution</h2>
<p>Following the supplied example in the bug documentation, consider the following iScript response:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Function</span> <span class="nf">IScript_Test</span><span class="o">()</span>
<span class="o">%</span><span class="nc">Response</span><span class="o">.</span><span class="na">WriteLine</span><span class="o">(</span><span class="s">"<p>Hello world</p>"</span><span class="o">);</span>
<span class="nc">End</span><span class="o">-</span><span class="nc">Function</span><span class="o">;</span>
</code></pre></div></div>
<p>If you were to create a CREF to represent this iScript function and add it to a Navigation Collection it would show up in the left hand navigation pane just fine when rendered in a Fluid Navigation Collection style Activity Guide. However, once you select to view the iScript content in the Fluid Navigation Collection the navigation will lock up.</p>
<p>You can alter the response of the iScript as follows to prevent the navigation from locking up:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Function</span> <span class="nf">IScript_Test</span><span class="o">()</span>
<span class="o">%</span><span class="nc">Response</span><span class="o">.</span><span class="na">WriteLine</span><span class="o">(</span><span class="s">"<script>var bLoadCompleted = true;</script>"</span><span class="o">);</span>
<span class="o">%</span><span class="nc">Response</span><span class="o">.</span><span class="na">WriteLine</span><span class="o">(</span><span class="s">"<p>Hello world</p>"</span><span class="o">);</span>
<span class="nc">End</span><span class="o">-</span><span class="nc">Function</span><span class="o">;</span>
</code></pre></div></div>
<h2 id="conclusion">Conclusion</h2>
<p>As I mentioned above, iScripts are great if your requirement is to have complete control over response content for the applications you build. You can create applications with amazing user interfaces and performance with iScripts, but this comes at the cost of having to understand and ensure that the response content conforms to the requirements that the higher level system components expect.</p>Colton FischerThere is a pesky bug that has been a thorn in my side for quite some time. The issue is the inability to use iScript based Content References in Fluid Navigation Collections. Viewing an iScript CREF in a Fluid Nav Collection results in the left hand side navigation locking up. This is a documented bug that does not have a solution listed.Server Side Logs in the Browser Console2020-06-20T16:09:03+00:002020-06-20T16:09:03+00:00http://peoplesoftmods.com/utilities/server-side-logs-in-the-browser-console<p>I did a post last year on the topic of <a href="https://www.peoplesoftmods.com/tips-and-tricks/fluid-component-logging-techniques/">Fluid Component Logging</a> that demonstrated techniques to view server side log statements in the browser console. The techniques relied on using the <code class="language-plaintext highlighter-rouge">AddOnLoadScript</code> function to inject <code class="language-plaintext highlighter-rouge">console.log()</code> JavaScript statements at debug time. I have come to find that the ability to view log statements in the browser console offers a more productive way to debug PeopleSoft applications versus using things like <code class="language-plaintext highlighter-rouge">MessageBox</code> or file-based logging. The problem with the techniques that I demonstrated is that they are limited to only working on Fluid Components and you have to write a bit of boilerplate code to log anything other than simple data types. I have written a couple of libraries that address both of these issues that can allow for an easy to use logging solution.</p>
<h2 id="console-logging-with-ps-chrome-logger">Console Logging with PS-Chrome-Logger</h2>
<p>I went down several paths in search for an intuitive way to be able to globally write server side log statements that show up in the browser console. I came up with working prototypes that involved using some interesting technology such as servlet filters and websockets. While these prototypes worked, they felt a bit overly complicated.</p>
<p>I ended up settling on a solution that requires a browser extension called <a href="https://chrome.google.com/webstore/detail/chrome-logger/noaneddfkdjfnfdakjjmocngnfkfehhd?hl=en">Chrome Logger</a>. I am not exactly thrilled on the idea of requiring the use of a specific browser and extension to achieve global console logging in PeopleSoft applications, but it works really well. The Chrome Logger project has a <a href="https://craig.is/writing/chrome-logger/techspecs">well written spec</a> that makes it easy to write a compatible server side implementation. You can check out my PeopleCode implementation of Chrome Logger on GitHub - <a href="https://github.com/coltonfischer/ps-chrome-logger">PS-Chrome-Logger</a>.</p>
<p>The PS-Chrome-Logger project (and browser extension) provides a way to perform console logging for any PeopleSoft application page where the <code class="language-plaintext highlighter-rouge">%Response</code> object is available. The <code class="language-plaintext highlighter-rouge">Log</code> Method of the <code class="language-plaintext highlighter-rouge">PSM_CHROME_LOGGER:Console</code> class takes a <code class="language-plaintext highlighter-rouge">JsonNode</code> parameter that contains the data to log to the console. Creating a <code class="language-plaintext highlighter-rouge">JsonNode</code> to represent a simple value (String, Number, Boolean, etc.) is trivial, but there is a bit of work needed for object and array structures and it is not practical to have to manually perform this serialization logic at debug time. For example, consider a scenario where you might want to log the Rowset returned for <code class="language-plaintext highlighter-rouge">GetLevel0()</code> to understand the Component buffer contents. This will require a lot of logic (loops, etc.) to output the Rowset data structure in a human-readable format.</p>
<p>In the next section I will discuss a solution to dynamically serialize PeopleCode data/object types to <code class="language-plaintext highlighter-rouge">JsonNode</code> to provide an easy way to log complex data structures.</p>
<h2 id="json-conversion-with-ps-jsonify">JSON Conversion with PS-Jsonify</h2>
<p>I created a JSON serialization library called <a href="http://www.coltonfischer.com/ps-jsonify/">PS-Jsonify</a>. This library allows you to seamlessly convert PeopleCode data/object types to the native <code class="language-plaintext highlighter-rouge">JsonObject</code>, <code class="language-plaintext highlighter-rouge">JsonArray</code>, and <code class="language-plaintext highlighter-rouge">JsonNode</code> object types. One of the main drivers for building this library was to provide an easy way to convert dynamic data structures to JSON strings for the purpose of debugging PeopleSoft applications in the browser with PS-Chrome-Logger.</p>
<p>Below is an example of how PS-Jsonify can be used with PS-Chrome-Logger to log the contents of an Application Class object to the browser console.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Local</span> <span class="nl">PTPP_COLLECTIONS:</span><span class="nc">Shortcut</span> <span class="o">&</span><span class="n">oCref</span> <span class="o">=</span> <span class="n">create</span> <span class="nl">PTPP_COLLECTIONS:</span><span class="nc">Shortcut</span><span class="o">(%</span><span class="nc">Portal</span><span class="o">,</span> <span class="s">"PT_CHANGE_PASSWORD_GBL"</span><span class="o">);</span>
<span class="cm">/* Begin Debug */</span>
<span class="nc">Local</span> <span class="nl">PSM_JSON:</span><span class="nc">Node</span> <span class="o">&</span><span class="n">oJson</span> <span class="o">=</span> <span class="n">create</span> <span class="nl">PSM_JSON:</span><span class="nc">Node</span><span class="o">();</span>
<span class="o">&</span><span class="n">oJson</span><span class="o">.</span><span class="na">SetClass</span><span class="o">(&</span><span class="n">oCref</span><span class="o">);</span>
<span class="nc">Local</span> <span class="nl">PSM_CHROME_LOGGER:</span><span class="nc">Console</span> <span class="o">&</span><span class="n">oConsole</span> <span class="o">=</span> <span class="n">create</span> <span class="nl">PSM_CHROME_LOGGER:</span><span class="nc">Console</span><span class="o">();</span>
<span class="o">&</span><span class="n">oConsole</span><span class="o">.</span><span class="na">Log</span><span class="o">(&</span><span class="n">oJson</span><span class="o">);</span>
<span class="cm">/* End Debug */</span>
</code></pre></div></div>
<p>The above example resulted in 6 lines of code (2 for the class imports) just to output a single debug statement. There could’ve been some syntactical sugar added to reduce the lines of code, but there is no denying the fact that writing a debug statement in that manner is a chore in itself.</p>
<p>In the next section I will demonstrate how you can create an Application Class facade that allows for a log statement to be written in a single line of code.</p>
<h2 id="creating-a-logging-facade">Creating a Logging Facade</h2>
<p>The PS-Chrome-Logger and PS-Jsonify libraries can be consolidated into a single Application Class to provide a consumer-friendly facade that allows for log statements to be written in one line of code. Let me demonstrate.</p>
<p>I created an Application Package named “L” and added an Application Class named “O”. <em>The significance of these oddly short names will become apparent</em>.</p>
<p>I implemented the “O” Application Class as follows:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">PSM_JSON</span><span class="o">:</span><span class="nc">Node</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">PSM_CHROME_LOGGER</span><span class="o">:</span><span class="nc">Console</span><span class="o">;</span>
<span class="kd">class</span> <span class="nc">O</span>
<span class="n">method</span> <span class="nf">G</span><span class="o">(&</span><span class="n">paAny</span> <span class="nc">As</span> <span class="n">any</span><span class="o">);</span>
<span class="n">end</span><span class="o">-</span><span class="kd">class</span><span class="err">;</span>
<span class="nc">method</span> <span class="no">G</span>
<span class="o">/+</span> <span class="o">&</span><span class="n">paAny</span> <span class="n">as</span> <span class="nc">Any</span> <span class="o">+/</span>
<span class="nc">Local</span> <span class="nl">PSM_JSON:</span><span class="nc">Node</span> <span class="o">&</span><span class="n">oJsonNode</span> <span class="o">=</span> <span class="n">create</span> <span class="nl">PSM_JSON:</span><span class="nc">Node</span><span class="o">();</span>
<span class="o">&</span><span class="n">oJsonNode</span><span class="o">.</span><span class="na">SetValue</span><span class="o">(&</span><span class="n">paAny</span><span class="o">);</span>
<span class="nc">Local</span> <span class="nl">PSM_CHROME_LOGGER:</span><span class="nc">Console</span> <span class="o">&</span><span class="n">oConsole</span> <span class="o">=</span> <span class="n">create</span> <span class="nl">PSM_CHROME_LOGGER:</span><span class="nc">Console</span><span class="o">();</span>
<span class="o">&</span><span class="n">oConsole</span><span class="o">.</span><span class="na">Log</span><span class="o">(&</span><span class="n">oJsonNode</span><span class="o">);</span>
<span class="n">end</span><span class="o">-</span><span class="n">method</span><span class="o">;</span>
</code></pre></div></div>
<p>This class combines the JSON serialization logic with the Chrome Logger statement output and exposes the functionality with a consumer method named “G” that takes an <code class="language-plaintext highlighter-rouge">Any</code> value to log.</p>
<p class="notice--warning"><strong>Note</strong>
The <a href="http://www.coltonfischer.com/ps-jsonify/JSON%20Node/Methods/SetValue.html">SetValue</a> method is used in the logging facade. This method takes an <code class="language-plaintext highlighter-rouge">Any</code> value and is capable of converting all conventional data types. It’s support for object data type conversion is limited to the <a href="http://www.coltonfischer.com/ps-jsonify/JSON%20Node/">object types listed here</a>.</p>
<p>The above class can be used to output a log statement with the following single line of code:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span><span class="nc">CreateObject</span><span class="o">(</span><span class="s">"L:O"</span><span class="o">)).</span><span class="na">G</span><span class="o">(</span><span class="s">"Hello from the server"</span><span class="o">);</span> <span class="cm">/* @debug */</span>
</code></pre></div></div>
<p>This above statement is certainly not easy on the eyes, but there is a method to the madness. The <code class="language-plaintext highlighter-rouge">CreateObject</code> method is used to dynamically create the logging facade class so that I am not burdened with having to import the class anytime that I want to log something. The single character package/class/method names are not necessary, but they do allow for minimal typing when writing the log statement. The practice of using the commented <code class="language-plaintext highlighter-rouge">@debug</code> annotation at the end of the log statements can assist in post debugging cleanup.</p>Colton FischerI did a post last year on the topic of Fluid Component Logging that demonstrated techniques to view server side log statements in the browser console. The techniques relied on using the AddOnLoadScript function to inject console.log() JavaScript statements at debug time. I have come to find that the ability to view log statements in the browser console offers a more productive way to debug PeopleSoft applications versus using things like MessageBox or file-based logging. The problem with the techniques that I demonstrated is that they are limited to only working on Fluid Components and you have to write a bit of boilerplate code to log anything other than simple data types. I have written a couple of libraries that address both of these issues that can allow for an easy to use logging solution.Getting Started with Pivet on Windows2020-05-16T19:40:12+00:002020-05-16T19:40:12+00:00http://peoplesoftmods.com/utilities/getting-started-with-pivet-on-windows<p><a href="https://github.com/tslater2006/Pivet">Pivet</a> is an open source PeopleSoft versioning tool that allows for git-based version control for PeopleSoft definitions. I originally installed Pivet in one of my demo instances last year and have been making use of it to backup/document the PeopleCode for some of my <a href="https://github.com/coltonfischer">open source projects</a>. I recently deployed a new environment and wanted to get Pivet up and running in it. In this post I will document the steps that I took to get the latest version (0.2 at the time of writing) of Pivet installed and working in my Windows environment.</p>
<h3 id="download-pivet">Download Pivet</h3>
<p>Navigate to the <a href="https://github.com/tslater2006/Pivet/releases">Releases tab</a> of the Pivet GitHub repository and download the Windows self contained build zip file.</p>
<p><a href="/assets/images/2020/05/Download.png"><img src="/assets/images/2020/05/Download.png" alt="Download" /></a></p>
<p>Extract the zip file into a local directory. In this example I extracted the Pivet files into <code class="language-plaintext highlighter-rouge">C:\Temp\Pivet</code>.</p>
<p><a href="/assets/images/2020/05/Extract.png"><img src="/assets/images/2020/05/Extract.png" alt="Extract" /></a></p>
<h3 id="create-config-file">Create Config File</h3>
<p>Pivet needs a <code class="language-plaintext highlighter-rouge">config.json</code> file defined in the directory where <code class="language-plaintext highlighter-rouge">pivet.exe</code> resides. This config file will define <em>Environment</em> and <em>Profile</em> information. Environments will contain the database connection details while Profiles will define which objects need to be processed and exported. Pivet allows for multiple Environments and Profiles to be defined.</p>
<p>Pivet is capable of generating the properly formatted <code class="language-plaintext highlighter-rouge">config.json</code> by running <code class="language-plaintext highlighter-rouge">pivet.exe -b</code>. This “config builder mode” is a guided process that will prompt you for the variable input that is needed to create an Environment and Profile.</p>
<p class="notice--warning"><strong>Note</strong>
I came across an error when I first ran the <code class="language-plaintext highlighter-rouge">pivet.exe -b</code> command. See the <a href="#troubleshooting-tips">Troubleshooting Tips</a> for more details.</p>
<p>Here is the variable input that I provided to the program to create an Environment and Profile that is capable of backing up a single PeopleSoft project called <code class="language-plaintext highlighter-rouge">PIVET_DEMO</code>.</p>
<p><a href="/assets/images/2020/05/Input.png"><img src="/assets/images/2020/05/Input.png" alt="Input" /></a></p>
<p>Most of the config values that the process asks for is self explanatory with a couple of exceptions. I was not sure what the <code class="language-plaintext highlighter-rouge">Schema</code> value refers to so I left it blank. The <code class="language-plaintext highlighter-rouge">TNS_ADMIN</code> value is simply the path the the <code class="language-plaintext highlighter-rouge">tnsnames.ora</code> file and the <code class="language-plaintext highlighter-rouge">TNS</code> value is the service name that contains the database connection details for the environment that you want to connect to.</p>
<p><a href="/assets/images/2020/05/TNS_ADMIN.png"><img src="/assets/images/2020/05/TNS_ADMIN.png" alt="TNS_ADMIN" /></a></p>
<p>This was the <code class="language-plaintext highlighter-rouge">config.json</code> file that the process generated for this example:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{
"Environments": [
{
"Name": "PIVET_DEMO_ENV",
"Connection": {
"Provider": "Bootstrap",
"TNS": "PSFTDB",
"TNS_ADMIN": "C:\\psft\\db\\",
"Schema": "",
"BootstrapParameters": {
"User": "SYSADM",
"EncryptedPassword": "Q0qXSEOj"
}
}
}
],
"Profiles": [
{
"Name": "PIVET_DEMO_PROFILE",
"DataProviders": [
"RawDataProcessor",
"HTMLProcessor",
"MessageCatalogProcessor",
"PeopleCodeProcessor",
"SQLProcessor",
"StylesheetProcessor",
"TranslateValueProcessor"
],
"Filters": {
"Projects": [
"PIVET_DEMO"
],
"Prefixes": [],
"IncludeOprids": [],
"ExcludeOprids": [],
"MessageCatalogs": [],
"RawData": []
}
}
],
"Jobs": []
}
</code></pre></div></div>
<p>The above config works well for an example, but it does not demonstrate the strength that Profiles have to offer. Check out the <a href="https://github.com/tslater2006/Pivet/releases/download/0.2/sample-config.json">sample-config.json</a> file in the GitHub repository to get an understanding of how to configure more complex Profiles.</p>
<h3 id="create-a-job">Create a Job</h3>
<p>Now that you have an example Environment and Profile configured, you will need to create a <em>Job</em>. A Job is specified in the <code class="language-plaintext highlighter-rouge">config.json</code> and it is what prompts Pivet to process a Profile when <code class="language-plaintext highlighter-rouge">pivet.exe</code> is ran. Pivet allows for multiple Jobs to be defined.</p>
<p>Here is an example Job config to export the objects from the <code class="language-plaintext highlighter-rouge">PIVET_DEMO</code> App Designer project to a local directory. Notice how you reference the Environment and Profile name that was created in the previous step.</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"Name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"PIVET_DEMO_JOB"</span><span class="p">,</span><span class="w">
</span><span class="nl">"EnvironmentName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"PIVET_DEMO_ENV"</span><span class="p">,</span><span class="w">
</span><span class="nl">"ProfileName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"PIVET_DEMO_PROFILE"</span><span class="p">,</span><span class="w">
</span><span class="nl">"OutputFolder"</span><span class="p">:</span><span class="w"> </span><span class="s2">"C:</span><span class="se">\\</span><span class="s2">temp</span><span class="se">\\</span><span class="s2">PIVET_DEMO</span><span class="se">\\</span><span class="s2">src"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>This object can be added to the <code class="language-plaintext highlighter-rouge">Jobs</code> array within the <code class="language-plaintext highlighter-rouge">config.json</code> file</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"Environments"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"Name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"PIVET_DEMO_ENV"</span><span class="p">,</span><span class="w">
</span><span class="nl">"Connection"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"Provider"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Bootstrap"</span><span class="p">,</span><span class="w">
</span><span class="nl">"TNS"</span><span class="p">:</span><span class="w"> </span><span class="s2">"PSFTDB"</span><span class="p">,</span><span class="w">
</span><span class="nl">"TNS_ADMIN"</span><span class="p">:</span><span class="w"> </span><span class="s2">"C:</span><span class="se">\\</span><span class="s2">psft</span><span class="se">\\</span><span class="s2">db</span><span class="se">\\</span><span class="s2">"</span><span class="p">,</span><span class="w">
</span><span class="nl">"Schema"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
</span><span class="nl">"BootstrapParameters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"User"</span><span class="p">:</span><span class="w"> </span><span class="s2">"SYSADM"</span><span class="p">,</span><span class="w">
</span><span class="nl">"EncryptedPassword"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Q0qXSEOj"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"Profiles"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"Name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"PIVET_DEMO_PROFILE"</span><span class="p">,</span><span class="w">
</span><span class="nl">"DataProviders"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"RawDataProcessor"</span><span class="p">,</span><span class="w">
</span><span class="s2">"HTMLProcessor"</span><span class="p">,</span><span class="w">
</span><span class="s2">"MessageCatalogProcessor"</span><span class="p">,</span><span class="w">
</span><span class="s2">"PeopleCodeProcessor"</span><span class="p">,</span><span class="w">
</span><span class="s2">"SQLProcessor"</span><span class="p">,</span><span class="w">
</span><span class="s2">"StylesheetProcessor"</span><span class="p">,</span><span class="w">
</span><span class="s2">"TranslateValueProcessor"</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"Filters"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"Projects"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="s2">"PIVET_DEMO"</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"Prefixes"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
</span><span class="nl">"IncludeOprids"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
</span><span class="nl">"ExcludeOprids"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
</span><span class="nl">"MessageCatalogs"</span><span class="p">:</span><span class="w"> </span><span class="p">[],</span><span class="w">
</span><span class="nl">"RawData"</span><span class="p">:</span><span class="w"> </span><span class="p">[]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">],</span><span class="w">
</span><span class="nl">"Jobs"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"Name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"PIVET_DEMO_JOB"</span><span class="p">,</span><span class="w">
</span><span class="nl">"EnvironmentName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"PIVET_DEMO_ENV"</span><span class="p">,</span><span class="w">
</span><span class="nl">"ProfileName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"PIVET_DEMO_PROFILE"</span><span class="p">,</span><span class="w">
</span><span class="nl">"OutputFolder"</span><span class="p">:</span><span class="w"> </span><span class="s2">"C:</span><span class="se">\\</span><span class="s2">temp</span><span class="se">\\</span><span class="s2">PIVET_DEMO</span><span class="se">\\</span><span class="s2">src"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>Now when <code class="language-plaintext highlighter-rouge">pivet.exe</code> is ran, the program picks up the Job config and processes the Profile. In this example the <code class="language-plaintext highlighter-rouge">PIVET_DEMO</code> App Designer project only contained PeopleCode object types and this is reflected in the program output.</p>
<p><a href="/assets/images/2020/05/Run.png"><img src="/assets/images/2020/05/Run.png" alt="Run" /></a></p>
<p>The exported objects can be viewed in the <code class="language-plaintext highlighter-rouge">OutputFolder</code> folder defined in the Job config.</p>
<h4 id="more-on-jobs">More on Jobs</h4>
<p>The above Job was configured to only save the objects to a local directory and not a remote git repository. If you would like for the objects to be automatically pushed to a remote repository when running Pivet, then you will need to specify the <code class="language-plaintext highlighter-rouge">Repository</code> within the Job. I personally don’t make use this functionality, but you can check out the <a href="https://github.com/tslater2006/Pivet/releases/download/0.2/sample-config.json">sample-config.json</a> to see how to format the config. The biggest gotcha for this is to specify the git user password as an encrypted string. You will need to run <code class="language-plaintext highlighter-rouge">pivet.exe -e</code> to run the guided process to encrypt the git user password.</p>
<p class="notice--success"><strong>Note</strong>
The <code class="language-plaintext highlighter-rouge">pivet.exe -e</code> encryption process can also be used to encrypt the database password to be specified for the <code class="language-plaintext highlighter-rouge">EncryptedPassword</code> property in the Environment config. This can be helpful for if the database password ever changes for an Environment.</p>
<h3 id="troubleshooting-tips">Troubleshooting Tips</h3>
<p>I ran into a minor error when attempting to run <code class="language-plaintext highlighter-rouge">pivet.exe</code> for the first time. It is possible that this error was specific to the environment that I was working in.</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">Error:
An assembly specified in the application dependencies manifest (Pivet.deps.json) was not found:
package: 'LibGit2Sharp.NativeBinaries', version: '2.0.289'
path: 'runtimes/win-x64/native/git2-7ce88e6.pdb'
</span></code></pre></div></div>
<h5 id="solution">Solution</h5>
<p>I removed the <code class="language-plaintext highlighter-rouge">runtimes/win-x64/native/git2-7ce88e6.pdb</code> property on line 878 in the <code class="language-plaintext highlighter-rouge">Pivet.deps.json</code> file.</p>
<p><a href="/assets/images/2020/05/Remove.png"><img src="/assets/images/2020/05/Remove.png" alt="Remove" /></a></p>Colton FischerPivet is an open source PeopleSoft versioning tool that allows for git-based version control for PeopleSoft definitions. I originally installed Pivet in one of my demo instances last year and have been making use of it to backup/document the PeopleCode for some of my open source projects. I recently deployed a new environment and wanted to get Pivet up and running in it. In this post I will document the steps that I took to get the latest version (0.2 at the time of writing) of Pivet installed and working in my Windows environment.Access Local PS Apps Publicly2020-05-12T01:53:33+00:002020-05-12T01:53:33+00:00http://peoplesoftmods.com/tips-and-tricks/access-local-ps-apps-publicly<p>If you are like me and run PeopleSoft applications on your personal machine, there are times where you might want to share a feature or functionality that you have developed on the local instance to a user that is not on the LAN. A popular solution for exposing local web apps over a public endpoint is <a href="https://ngrok.com/">Ngrok</a>. I found that the free tier of Ngrok is usable for exposing a local PeopleSoft instance to the public, but it poses some limitations around the amount of connections per minute that makes it challenging to use at times. I found several Ngrok alternatives online and one particularly interesting one was <a href="https://docs.inlets.dev/">Inlets</a>. Inlets does not pose any sort of limitations, but it does require you to run your own “exit node”. In this post I will demonstrate how to host an Inlets exit node for free using Heroku that will allow for your local PeopleSoft application to be accessed publicly.</p>
<h3 id="create-heroku-account">Create Heroku Account</h3>
<p>If you do not already have a Heroku account, then you will need to <a href="https://signup.heroku.com/">create one here</a>.</p>
<h3 id="deploy-to-heroku">Deploy to Heroku</h3>
<p>You can automatically setup the Heroku instance of the Inlets exit node by clicking the deploy button below.</p>
<p><a href="https://heroku.com/deploy?template=https://github.com/coltonfischer/inlets-heroku"><img src="https://www.herokucdn.com/deploy/button.svg" alt="Deploy" /></a></p>
<p>You will need to provide a name for the app. The name of the app will be used in the public URL that tunnels to your localhost. The URL format will be <code class="language-plaintext highlighter-rouge"><APP_NAME>.herokuapp.com</code>. In this example I will name the app <code class="language-plaintext highlighter-rouge">ps-inlet</code>.</p>
<p><a href="/assets/images/2020/05/Deploy.png"><img src="/assets/images/2020/05/Deploy.png" alt="Deploy App" /></a></p>
<p>Click the Deploy app button and the deployment process will create a shared secret key that will be used to secure the tunnel link. This key will need to be used on the host node (PeopleSoft) to be able to successfully connect.</p>
<h3 id="obtaining-the-secret-key">Obtaining the Secret Key</h3>
<p>After the deployment process completes, you will need to obtain the secret key that was generated during deployment. Navigate to the settings tab of the deployed app and click the <code class="language-plaintext highlighter-rouge">Reveal Config Vars</code> button.</p>
<p><a href="/assets/images/2020/05/Config.png"><img src="/assets/images/2020/05/Config.png" alt="Config Vars" /></a></p>
<p>Copy the value of the <code class="language-plaintext highlighter-rouge">TOKEN</code> variable to your clipboard. This value will be used in the next step.</p>
<h3 id="run-the-inlets-client">Run the Inlets Client</h3>
<p>The Inlets project offers a client application to be invoked on the host node to be able to connect to the exit node. This will allow for the public Heroku URL to be routed to your local PeopleSoft application. Download the client that is appropriate for your PeopleSoft application operating system.</p>
<ul>
<li><a href="https://github.com/inlets/inlets/releases/download/2.6.3/inlets.exe">Windows</a></li>
<li><a href="https://github.com/inlets/inlets/releases/tag/2.6.3">Linux</a></li>
</ul>
<p>Run the client specifying the following parameters:</p>
<ul>
<li>remote - Heroku URL where your exit node is running prefixed with “wss://”. Example <code class="language-plaintext highlighter-rouge">wss://<APP_NAME>.herokuapp.com</code>.</li>
<li>token - Shared secret key that was obtained the previous step.</li>
<li>upstream - IP address and port number to the local PeopleSoft PIA. Example: <code class="language-plaintext highlighter-rouge">http://127.0.0.1:8000</code>.</li>
</ul>
<p>Here is an example command to run the client on from a Windows PeopleSoft environment:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">inlets client --remote wss://ps-inlet.herokuapp.com --token 67770ad403322c281e32ea340b97811ced532c4584951e0c2e20c3ef28d2879a --upstream http://127.0.0.1:8000
</span></code></pre></div></div>
<p>This will create the connection tunnel from the Heroku URL to your localhost. You should see output similar to the following:</p>
<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="go">2020/05/11 18:55:01 Welcome to inlets.dev! Find out more at https://github.com/inlets/inlets
2020/05/11 18:55:01 Starting client - version 2.6.3
</span><span class="gp">2020/05/11 18:55:01 Upstream: =></span><span class="w"> </span>http://127.0.0.1:8000
<span class="go">2020/05/11 18:55:01 Token: "67770ad403322c281e32ea340b97811ced532c4584951e0c2e20c3ef28d2879a"
time="2020-05-11T18:55:01-05:00" level=info msg="Connecting to proxy" url="wss://ps-inlet.herokuapp.com/tunnel"
</span></code></pre></div></div>
<p>Now you can access your local PeopleSoft instance by pointing your browser to the public Heroku app URL (<code class="language-plaintext highlighter-rouge"><APP_NAME>.herokuapp.com</code>):</p>
<p><a href="/assets/images/2020/05/Public.png"><img src="/assets/images/2020/05/Public.png" alt="Public PeopleSoft" /></a></p>
<p>Simply exit the Inlets client when you are ready to close the connection tunnel.</p>Colton FischerIf you are like me and run PeopleSoft applications on your personal machine, there are times where you might want to share a feature or functionality that you have developed on the local instance to a user that is not on the LAN. A popular solution for exposing local web apps over a public endpoint is Ngrok. I found that the free tier of Ngrok is usable for exposing a local PeopleSoft instance to the public, but it poses some limitations around the amount of connections per minute that makes it challenging to use at times. I found several Ngrok alternatives online and one particularly interesting one was Inlets. Inlets does not pose any sort of limitations, but it does require you to run your own “exit node”. In this post I will demonstrate how to host an Inlets exit node for free using Heroku that will allow for your local PeopleSoft application to be accessed publicly.Extending Cloud Manager with OCI REST API2020-05-06T02:33:08+00:002020-05-06T02:33:08+00:00http://peoplesoftmods.com/cm/extending-cloud-manager-with-oci-rest-api<p>Cloud Manager is a powerful tool for managing PeopleSoft Environments running on Oracle Cloud Infrastructure (OCI). There are many great features in the latest release and I highly recommend listening to the <a href="https://psadmin.io/2020/01/31/222-cloud-manager-10-review/">Cloud Manager 10 Review</a> PeopleSoft Administrator Podcast episode to understand the features of the current Cloud Manager release. As mentioned by <a href="https://i-like-trains.blogspot.com/">Graham Smith</a> in the episode, the “Stop” environment feature does not stop the underlying virtual machine instance running in OCI. This is undesirable because you will still get charged for compute cycles even when the environment is “Stopped”. In this post I will discuss how the OCI REST API can be used to extend Cloud Manager to support functionality such as stopping OCI virtual machine instances.</p>
<h3 id="the-oci-rest-api">The OCI REST API</h3>
<p>OCI offers a robust REST API to perform various actions to the infrastructure running on OCI. One particularly useful function is the <a href="https://docs.cloud.oracle.com/en-us/iaas/api/#/en/iaas/20160918/Instance/InstanceAction">InstanceAction API</a>. This endpoint allows for performing Start, Stop, Reset, Soft Stop, and Soft Reset actions on an instance running in OCI. You need to pass the OCID of the instance in the URL path and specify the action (START, STOP, etc.) to perform as a query parameter.</p>
<p>One challenging aspect of consuming the OCI REST API is the <a href="https://docs.cloud.oracle.com/en-us/iaas/Content/API/Concepts/signingrequests.htm">Request Signatures</a> requirement. You must specify a specially-crafted <code class="language-plaintext highlighter-rouge">Authorization</code> Header for each API Request. Oracle provides good examples for crafting the Request Signature in various languages and even has a Java SDK for consuming the API. However, I found that there are some Java Classes delivered with Cloud Manager that can aid in the consumption of the OCI REST API from PeopleCode without the need to introduce additional server dependencies.</p>
<h3 id="cloud-manager-java-classes">Cloud Manager Java Classes</h3>
<p>If you take a look at the Application PeopleCode for Cloud Manager, you will notice a lot of <code class="language-plaintext highlighter-rouge">CreateJavaObject</code> calls. Cloud Manager uses Java Classes to do the heavy lifting required to support the various application features. There are a ton of Java Classes used in Cloud Manager and I will review the interesting/useful ones that can be of help in consuming the OCI REST API from PeopleCode.</p>
<h3 id="ocisettings-class">OCISettings Class</h3>
<p>The <code class="language-plaintext highlighter-rouge">com.peoplesoft.pa.cl.infrastructure.oci.OCISettings</code> Class can be used to obtain various settings for your Cloud Manager environment. This class holds values that are needed in determining the proper REST URL endpoint based on the deployment region as well as key information used to generate the API request signatures. To get an idea of the properties that this class has to offer, here is some code that generates a JSON object based on the <code class="language-plaintext highlighter-rouge">OCISettings</code> class:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joOciSettings</span> <span class="o">=</span> <span class="nc">CreateJavaObject</span><span class="o">(</span><span class="s">"com.peoplesoft.pa.cl.infrastructure.oci.OCISettings"</span><span class="o">);</span>
<span class="nc">Local</span> <span class="nc">JsonObject</span> <span class="o">&</span><span class="nc">Json</span> <span class="o">=</span> <span class="nc">CreateJsonObject</span><span class="o">();</span>
<span class="o">&</span><span class="nc">Json</span><span class="o">.</span><span class="na">AddProperty</span><span class="o">(</span><span class="s">"tenancyName"</span><span class="o">,</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getTenancyName</span><span class="o">());</span>
<span class="o">&</span><span class="nc">Json</span><span class="o">.</span><span class="na">AddProperty</span><span class="o">(</span><span class="s">"tenancyOCID"</span><span class="o">,</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getTenancyOCID</span><span class="o">());</span>
<span class="o">&</span><span class="nc">Json</span><span class="o">.</span><span class="na">AddProperty</span><span class="o">(</span><span class="s">"userName"</span><span class="o">,</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getUserName</span><span class="o">());</span>
<span class="o">&</span><span class="nc">Json</span><span class="o">.</span><span class="na">AddProperty</span><span class="o">(</span><span class="s">"userOCID"</span><span class="o">,</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getUserOCID</span><span class="o">());</span>
<span class="o">&</span><span class="nc">Json</span><span class="o">.</span><span class="na">AddProperty</span><span class="o">(</span><span class="s">"publicKeyFile"</span><span class="o">,</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getPublicKeyFile</span><span class="o">());</span>
<span class="o">&</span><span class="nc">Json</span><span class="o">.</span><span class="na">AddProperty</span><span class="o">(</span><span class="s">"privateKeyFile"</span><span class="o">,</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getPrivateKeyFile</span><span class="o">());</span>
<span class="o">&</span><span class="nc">Json</span><span class="o">.</span><span class="na">AddProperty</span><span class="o">(</span><span class="s">"privateKeyPassphrase"</span><span class="o">,</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getPrivateKeyPassphrase</span><span class="o">());</span>
<span class="o">&</span><span class="nc">Json</span><span class="o">.</span><span class="na">AddProperty</span><span class="o">(</span><span class="s">"publicKeyFingerprint"</span><span class="o">,</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getPublicKeyFingerprint</span><span class="o">());</span>
<span class="o">&</span><span class="nc">Json</span><span class="o">.</span><span class="na">AddProperty</span><span class="o">(</span><span class="s">"apiSigningPrivateKeyFile"</span><span class="o">,</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getApiSigningPrivateKeyFile</span><span class="o">());</span>
<span class="o">&</span><span class="nc">Json</span><span class="o">.</span><span class="na">AddProperty</span><span class="o">(</span><span class="s">"apiSigningPrivateKeyPassphrase"</span><span class="o">,</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getApiSigningPrivateKeyPassphrase</span><span class="o">());</span>
<span class="o">&</span><span class="nc">Json</span><span class="o">.</span><span class="na">AddProperty</span><span class="o">(</span><span class="s">"apiVersion"</span><span class="o">,</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getApiVersion</span><span class="o">());</span>
<span class="o">&</span><span class="nc">Json</span><span class="o">.</span><span class="na">AddProperty</span><span class="o">(</span><span class="s">"cmSSHPrivateKey"</span><span class="o">,</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getCmSSHPrivateKey</span><span class="o">());</span>
<span class="o">&</span><span class="nc">Json</span><span class="o">.</span><span class="na">AddProperty</span><span class="o">(</span><span class="s">"homeRegion"</span><span class="o">,</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getHomeRegion</span><span class="o">());</span>
<span class="o">&</span><span class="nc">Json</span><span class="o">.</span><span class="na">AddProperty</span><span class="o">(</span><span class="s">"deploymentRegion"</span><span class="o">,</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getDeploymentRegion</span><span class="o">());</span>
<span class="o">&</span><span class="nc">Json</span><span class="o">.</span><span class="na">AddProperty</span><span class="o">(</span><span class="s">"linuxImageOCID"</span><span class="o">,</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getLinuxImageOCID</span><span class="o">());</span>
<span class="o">&</span><span class="nc">Json</span><span class="o">.</span><span class="na">AddProperty</span><span class="o">(</span><span class="s">"windowsImageOCID"</span><span class="o">,</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getWindowsImageOCID</span><span class="o">());</span>
<span class="o">&</span><span class="nc">Json</span><span class="o">.</span><span class="na">AddProperty</span><span class="o">(</span><span class="s">"windowsImagePassword"</span><span class="o">,</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getWindowsImagePassword</span><span class="o">());</span>
<span class="nc">Return</span> <span class="o">&</span><span class="nc">Json</span><span class="o">.</span><span class="na">ToString</span><span class="o">();</span>
</code></pre></div></div>
<h3 id="restendpointsimpl-class">RestEndpointsImpl Class</h3>
<p>The <code class="language-plaintext highlighter-rouge">com.peoplesoft.pa.cl.infrastructure.oci.util.RestEndpointsImpl</code> Class can be used to generate the base URL for some of the different OCI APIs. Here is an example of how this class can be used with the <code class="language-plaintext highlighter-rouge">OCISettings</code> class to return the base URL to the <a href="https://docs.cloud.oracle.com/en-us/iaas/api/#/en/iaas/20160918/">Core Services API</a>:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joOciSettings</span> <span class="o">=</span> <span class="nc">CreateJavaObject</span><span class="o">(</span><span class="s">"com.peoplesoft.pa.cl.infrastructure.oci.OCISettings"</span><span class="o">);</span>
<span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joRestEndpoints</span> <span class="o">=</span> <span class="nc">CreateJavaObject</span><span class="o">(</span><span class="s">"com.peoplesoft.pa.cl.infrastructure.oci.util.RestEndpointsImpl"</span><span class="o">,</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getDeploymentRegion</span><span class="o">());</span>
<span class="nc">Return</span> <span class="s">"https://"</span> <span class="o">|</span> <span class="o">&</span><span class="n">joRestEndpoints</span><span class="o">.</span><span class="na">coreBase</span><span class="o">()</span> <span class="o">|</span> <span class="s">"/"</span> <span class="o">|</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getApiVersion</span><span class="o">();</span>
</code></pre></div></div>
<h3 id="ocirestservicesimpl-class">OCIRestServicesImpl Class</h3>
<p>The <code class="language-plaintext highlighter-rouge">com.peoplesoft.pa.cl.infrastructure.oci.OCIRestServicesImpl</code> Class is a factory for obtaining implementations of the different OCI APIs. An example OCI API implementation class is the <code class="language-plaintext highlighter-rouge">com.peoplesoft.pa.cl.infrastructure.oci.rest.OCICoreServicesImpl</code> Class which implements the Core Services API. The <code class="language-plaintext highlighter-rouge">OCICoreServicesImpl</code> provides an avenue to easily consume a handful of the Core Service APIs. Here is an example of using the <code class="language-plaintext highlighter-rouge">OCIRestServicesImpl</code> class to obtain an instance of the <code class="language-plaintext highlighter-rouge">OCICoreServicesImpl</code> class to call the <a href="https://docs.cloud.oracle.com/en-us/iaas/api/#/en/iaas/20160918/Instance/GetInstance">GetInstance</a> API endpoint:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joOCICoreServices</span> <span class="o">=</span> <span class="nc">GetJavaClass</span><span class="o">(</span><span class="s">"com.peoplesoft.pa.cl.infrastructure.oci.rest.OCICoreServicesImpl"</span><span class="o">);</span>
<span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joOCIRestServices</span> <span class="o">=</span> <span class="nc">CreateJavaObject</span><span class="o">(</span><span class="s">"com.peoplesoft.pa.cl.infrastructure.oci.OCIRestServicesImpl"</span><span class="o">);</span>
<span class="o">&</span><span class="n">joOCICoreServices</span> <span class="o">=</span> <span class="o">&</span><span class="n">joOCIRestServices</span><span class="o">.</span><span class="na">getCoreServices</span><span class="o">();</span>
<span class="nc">Local</span> <span class="n">string</span> <span class="o">&</span><span class="n">sOCID</span> <span class="o">=</span> <span class="s">"ocid1.instance.oc1.phx.abuw4ljrlsfiqw6vzzxb43vyypt4pkodawglp3wqxjqofakrwvou52gb6s5a"</span><span class="o">;</span>
<span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joJsonObject</span> <span class="o">=</span> <span class="o">&</span><span class="n">joOCICoreServices</span><span class="o">.</span><span class="na">getInstance</span><span class="o">(&</span><span class="n">sOCID</span><span class="o">);</span>
<span class="nc">Return</span> <span class="o">&</span><span class="n">joJsonObject</span><span class="o">.</span><span class="na">toString</span><span class="o">();</span>
</code></pre></div></div>
<h3 id="ocirestclient-class">OCIRestClient Class</h3>
<p>The <code class="language-plaintext highlighter-rouge">com.peoplesoft.pa.cl.infrastructure.oci.OCIRestClient</code> Class can be used to make generic REST requests to any of the OCI APIs. This class is useful because it provides flexibility and it performs the request signatures on all requests. The <code class="language-plaintext highlighter-rouge">OCIRestClient</code> offers the following methods for making generic REST requests to OCI API endpoints: doGet, doPost, doPut, and doDelete. This class offers a lot of potential and really deserves a post on its own. I will say that I had success in using this class to perform GET requests to the OCI API, but I experienced inconsistent results when attempting to perform a POST request. I will wait to document this class any further until I prove it to be fully functional.</p>
<h3 id="requestsignatureimpl-class">RequestSignatureImpl Class</h3>
<p>The <code class="language-plaintext highlighter-rouge">com.peoplesoft.pa.cl.infrastructure.oci.RequestSignatureImpl</code> Class can be used to perform the request signature on an arbitrary OCI API request. The <code class="language-plaintext highlighter-rouge">RequestSignatureImpl</code> class offers a <code class="language-plaintext highlighter-rouge">sign</code> method that takes in an <code class="language-plaintext highlighter-rouge">HttpRequestBase</code> object parameter. The class adds the properly formatted <code class="language-plaintext highlighter-rouge">Authorization</code> header to the request for it to then be sent to an OCI API endpoint. This class can be used in conjunction with the other Java classes mentioned above to make generic requests to the OCI API. I really didn’t want to have to resort to using this class, but the fact that I couldn’t get the <code class="language-plaintext highlighter-rouge">OCIRestClient</code> to perform POST requests left me no other choice. Here is some sample code that can be used to extend Cloud Manager to stop a VM instance using the the <a href="https://docs.cloud.oracle.com/en-us/iaas/api/#/en/iaas/20160918/Instance/InstanceAction">InstanceAction API</a> endpoint:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Local</span> <span class="n">string</span> <span class="o">&</span><span class="n">sInstanceOCID</span> <span class="o">=</span> <span class="s">"ocid1.instance.oc1.phx.abuw4ljrlsfiqw6vzzxb43vyypt4pkodawglp3wqxjqofakrwvou52gb6s5a"</span><span class="o">;</span>
<span class="nc">Local</span> <span class="n">string</span> <span class="o">&</span><span class="n">sAction</span> <span class="o">=</span> <span class="s">"STOP"</span><span class="o">;</span>
<span class="n">rem</span> <span class="nc">Local</span> <span class="n">string</span> <span class="o">&</span><span class="n">sAction</span> <span class="o">=</span> <span class="s">"START"</span><span class="o">;</span>
<span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joOciSettings</span> <span class="o">=</span> <span class="nc">CreateJavaObject</span><span class="o">(</span><span class="s">"com.peoplesoft.pa.cl.infrastructure.oci.OCISettings"</span><span class="o">);</span>
<span class="nc">Local</span> <span class="n">string</span> <span class="o">&</span><span class="n">sTenancyOCID</span> <span class="o">=</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getTenancyOCID</span><span class="o">();</span>
<span class="nc">Local</span> <span class="n">string</span> <span class="o">&</span><span class="n">sUserOCID</span> <span class="o">=</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getUserOCID</span><span class="o">();</span>
<span class="nc">Local</span> <span class="n">string</span> <span class="o">&</span><span class="n">sPublicKeyFingerprint</span> <span class="o">=</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getPublicKeyFingerprint</span><span class="o">();</span>
<span class="nc">Local</span> <span class="n">string</span> <span class="o">&</span><span class="n">sApiSigningPrivateKeyFile</span> <span class="o">=</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getApiSigningPrivateKeyFile</span><span class="o">();</span>
<span class="nc">Local</span> <span class="n">string</span> <span class="o">&</span><span class="n">sApiSigningPrivateKeyPassphrase</span> <span class="o">=</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getApiSigningPrivateKeyPassphrase</span><span class="o">();</span>
<span class="nc">Local</span> <span class="n">string</span> <span class="o">&</span><span class="n">sDeploymentRegion</span> <span class="o">=</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getDeploymentRegion</span><span class="o">();</span>
<span class="nc">Local</span> <span class="n">string</span> <span class="o">&</span><span class="n">sApiVersion</span> <span class="o">=</span> <span class="o">&</span><span class="n">joOciSettings</span><span class="o">.</span><span class="na">getApiVersion</span><span class="o">();</span>
<span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joRestEndpoints</span> <span class="o">=</span> <span class="nc">CreateJavaObject</span><span class="o">(</span><span class="s">"com.peoplesoft.pa.cl.infrastructure.oci.util.RestEndpointsImpl"</span><span class="o">,</span> <span class="o">&</span><span class="n">sDeploymentRegion</span><span class="o">);</span>
<span class="nc">Local</span> <span class="n">string</span> <span class="o">&</span><span class="n">sRestBaseUrl</span> <span class="o">=</span> <span class="s">"https://"</span> <span class="o">|</span> <span class="o">&</span><span class="n">joRestEndpoints</span><span class="o">.</span><span class="na">coreBase</span><span class="o">()</span> <span class="o">|</span> <span class="s">"/"</span> <span class="o">|</span> <span class="o">&</span><span class="n">sApiVersion</span><span class="o">;</span>
<span class="nc">Local</span> <span class="n">string</span> <span class="o">&</span><span class="n">sInstanceActionUrl</span> <span class="o">=</span> <span class="o">&</span><span class="n">sRestBaseUrl</span> <span class="o">|</span> <span class="s">"/instances/"</span> <span class="o">|</span> <span class="o">&</span><span class="n">sInstanceOCID</span> <span class="o">|</span> <span class="s">"?action="</span> <span class="o">|</span> <span class="o">&</span><span class="n">sAction</span><span class="o">;</span>
<span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joHttpPost</span> <span class="o">=</span> <span class="nc">CreateJavaObject</span><span class="o">(</span><span class="s">"org.apache.http.client.methods.HttpPost"</span><span class="o">,</span> <span class="o">&</span><span class="n">sInstanceActionUrl</span><span class="o">);</span>
<span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joRequestSigner</span> <span class="o">=</span> <span class="nc">CreateJavaObject</span><span class="o">(</span><span class="s">"com.peoplesoft.pa.cl.infrastructure.oci.RequestSignatureImpl"</span><span class="o">,</span> <span class="o">&</span><span class="n">sApiSigningPrivateKeyFile</span><span class="o">,</span> <span class="o">&</span><span class="n">sApiSigningPrivateKeyPassphrase</span><span class="o">,</span> <span class="o">&</span><span class="n">sTenancyOCID</span> <span class="o">|</span> <span class="s">"/"</span> <span class="o">|</span> <span class="o">&</span><span class="n">sUserOCID</span> <span class="o">|</span> <span class="s">"/"</span> <span class="o">|</span> <span class="o">&</span><span class="n">sPublicKeyFingerprint</span><span class="o">);</span>
<span class="o">&</span><span class="n">joRequestSigner</span><span class="o">.</span><span class="na">sign</span><span class="o">(&</span><span class="n">joHttpPost</span><span class="o">);</span>
<span class="o">&</span><span class="n">joHttpPost</span><span class="o">.</span><span class="na">removeHeader</span><span class="o">(&</span><span class="n">joHttpPost</span><span class="o">.</span><span class="na">getFirstHeader</span><span class="o">(</span><span class="s">"content-length"</span><span class="o">));</span> <span class="cm">/* https://stackoverflow.com/questions/25182719 */</span>
<span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joHttpClientBuilder</span> <span class="o">=</span> <span class="nc">GetJavaClass</span><span class="o">(</span><span class="s">"org.apache.http.impl.client.HttpClientBuilder"</span><span class="o">);</span>
<span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joCloseableHttpClient</span> <span class="o">=</span> <span class="o">&</span><span class="n">joHttpClientBuilder</span><span class="o">.</span><span class="na">create</span><span class="o">().</span><span class="na">useSystemProperties</span><span class="o">().</span><span class="na">build</span><span class="o">();</span>
<span class="n">rem</span> <span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joResponse</span> <span class="o">=</span> <span class="o">&</span><span class="n">joCloseableHttpClient</span><span class="o">.</span><span class="na">execute</span><span class="o">(&</span><span class="n">joHttpPost</span><span class="o">);</span> <span class="cm">/* more than one overload matches */</span>
<span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joClass</span> <span class="o">=</span> <span class="nc">GetJavaClass</span><span class="o">(</span><span class="s">"java.lang.Class"</span><span class="o">);</span>
<span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joClassLoader</span> <span class="o">=</span> <span class="nc">GetJavaClass</span><span class="o">(</span><span class="s">"java.lang.ClassLoader"</span><span class="o">);</span>
<span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joCloseableHttpClientClass</span> <span class="o">=</span> <span class="o">&</span><span class="n">joClass</span><span class="o">.</span><span class="na">forName</span><span class="o">(</span><span class="s">"org.apache.http.impl.client.CloseableHttpClient"</span><span class="o">,</span> <span class="nc">False</span><span class="o">,</span> <span class="o">&</span><span class="n">joClassLoader</span><span class="o">.</span><span class="na">getSystemClassLoader</span><span class="o">());</span>
<span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joExecuteArgTypes</span> <span class="o">=</span> <span class="nc">CreateJavaObject</span><span class="o">(</span><span class="s">"java.lang.Class[]"</span><span class="o">,</span> <span class="nc">GetJavaClass</span><span class="o">(</span><span class="s">"org.apache.http.client.methods.HttpUriRequest"</span><span class="o">));</span>
<span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joExecuteMethod</span> <span class="o">=</span> <span class="o">&</span><span class="n">joCloseableHttpClientClass</span><span class="o">.</span><span class="na">getDeclaredMethod</span><span class="o">(</span><span class="s">"execute"</span><span class="o">,</span> <span class="o">&</span><span class="n">joExecuteArgTypes</span><span class="o">);</span>
<span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joExecuteArgs</span> <span class="o">=</span> <span class="nc">CreateJavaObject</span><span class="o">(</span><span class="s">"java.lang.Object[]"</span><span class="o">,</span> <span class="o">&</span><span class="n">joHttpPost</span><span class="o">);</span>
<span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joHttpResponseClass</span> <span class="o">=</span> <span class="o">&</span><span class="n">joClass</span><span class="o">.</span><span class="na">forName</span><span class="o">(</span><span class="s">"org.apache.http.HttpResponse"</span><span class="o">,</span> <span class="nc">False</span><span class="o">,</span> <span class="o">&</span><span class="n">joClassLoader</span><span class="o">.</span><span class="na">getSystemClassLoader</span><span class="o">());</span>
<span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joResponse</span> <span class="o">=</span> <span class="o">&</span><span class="n">joHttpResponseClass</span><span class="o">.</span><span class="na">cast</span><span class="o">(&</span><span class="n">joExecuteMethod</span><span class="o">.</span><span class="na">invoke</span><span class="o">(&</span><span class="n">joCloseableHttpClient</span><span class="o">,</span> <span class="o">&</span><span class="n">joExecuteArgs</span><span class="o">));</span>
<span class="nc">Return</span> <span class="nf">String</span><span class="o">(&</span><span class="n">joResponse</span><span class="o">.</span><span class="na">getStatusLine</span><span class="o">().</span><span class="na">getStatusCode</span><span class="o">());</span>
</code></pre></div></div>
<h3 id="closing-thoughts">Closing Thoughts</h3>
<p>I only touched on a small aspect of the OCI REST API in this post, but the API appears to offer complete control over your cloud infrastructure. Extending Cloud Manager with the OCI REST API offers organizations the ability to use a familiar technology (PeopleTools) to write custom automation tools to manage any infrastructure running on OCI. I think this is great because it will allow PeopleSoft developers to continue to leverage their current skillset as well as provide a consolidated system (UI) for managing cloud infrastructure.</p>Colton FischerCloud Manager is a powerful tool for managing PeopleSoft Environments running on Oracle Cloud Infrastructure (OCI). There are many great features in the latest release and I highly recommend listening to the Cloud Manager 10 Review PeopleSoft Administrator Podcast episode to understand the features of the current Cloud Manager release. As mentioned by Graham Smith in the episode, the “Stop” environment feature does not stop the underlying virtual machine instance running in OCI. This is undesirable because you will still get charged for compute cycles even when the environment is “Stopped”. In this post I will discuss how the OCI REST API can be used to extend Cloud Manager to support functionality such as stopping OCI virtual machine instances.PS-SwitchToken Bookmarklet Generator2020-03-25T06:00:01+00:002020-03-25T06:00:01+00:00http://peoplesoftmods.com/utilities/ps-switchtoken-generator<p>PS-SwitchToken is a <a href="https://github.com/coltonfischer/ps-switchtoken">JavaScript program</a> that allows
you to seamlessly switch users during a PeopleSoft session.</p>
<p>Use the form below to generate a PS-SwitchToken bookmarklet for your PeopleSoft environment. Input a title for
the bookmarklet and the Local Node Password and then click Generate.</p>
<script src="/assets/js/ps-switchtoken-generator.js"></script>
<form>
<fieldset>
Bookmark Title: <input id="title" type="text" size="30" value="PS-SwitchToken" /><br />
Node Password: <input id="password" type="password" value="PS" /><br />
</fieldset>
<a onclick="generateBookmarklet();" class="btn btn--info">Generate</a>
</form>
<p><b>Generated Bookmark: </b><a id="bookmarklet" href=""></a></p>
<p>Drag the generated link into your browser bookmark bar and invoke it from a PeopleSoft page in your environment.<br />
You will get a prompt to input the User ID that you want to switch to:</p>
<p><a href="/assets/images/2020/03/PS-SwitchToken.png"><img src="/assets/images/2020/03/PS-SwitchToken.png" alt="PS-SwitchToken" /></a></p>
<p>After inputting a valid User ID and clicking OK, the page will refresh and you will be logged in as the new user.</p>
<p class="notice--warning"><strong>Notice</strong> This utility may not work for all PeopleSoft environments. Check
out the <a href="https://github.com/coltonfischer/ps-switchtoken">GitHub Repository</a> for additional configuration
details and troubleshooting tips.</p>Colton FischerPS-SwitchToken is a JavaScript program that allows you to seamlessly switch users during a PeopleSoft session.Run Dynamic Code with Application Class Tester2020-03-22T06:00:01+00:002020-03-22T06:00:01+00:00http://peoplesoftmods.com/tips-and-tricks/run-dynamic-code-with-application-class-tester<p>I previously did a post that documented a utility I created that allows you to
<a href="https://www.peoplesoftmods.com/utilities/compile-and-run-peoplecode-online/">Compile and Run PeopleCode Online</a>.
This was a neat
tool, but it required importing an App Designer project to be able to achieve the functionality of
running dynamic PeopleCode in the PIA. It would be much better to be able to run dynamic PeopleCode in the PIA using
delivered tooling. Well it turns out that you actually can do this by making use of the Application Class Tester
and a delivered Application Class. Combining these two PeopleSoft deliverables allows for creating and running custom
Application Class PeopleCode all from the PIA.</p>
<p>Check out the video demonstration on how to do this:</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/5xOOtwjX58M" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="">
</iframe>
<hr />
<p>Being able to run dynamic PeopleCode from the PIA is a bit of a double-edged sword. I think it is quite
obvious the security implications that could occur if this technique were abused by a malicious user. On the other hand,
this technique could be used by a savvy developer to get out of a pinch. With all things considered, I think it would be best to not
allow any access to the Application Class Tester unless it is absolutely needed.</p>Colton FischerI previously did a post that documented a utility I created that allows you to Compile and Run PeopleCode Online. This was a neat tool, but it required importing an App Designer project to be able to achieve the functionality of running dynamic PeopleCode in the PIA. It would be much better to be able to run dynamic PeopleCode in the PIA using delivered tooling. Well it turns out that you actually can do this by making use of the Application Class Tester and a delivered Application Class. Combining these two PeopleSoft deliverables allows for creating and running custom Application Class PeopleCode all from the PIA.Exposing Undocumented PeopleCode Functions2019-10-24T06:00:01+00:002019-10-24T06:00:01+00:00http://peoplesoftmods.com/tips-and-tricks/exposing-undocumented-peoplecode-functions<p>There are some really useful built-in PeopleCode functions that are available to be consumed, but are not documented
in PeopleBooks. Good examples of these functions include the <code class="language-plaintext highlighter-rouge">CreateJsonXxx</code> functions used for creating and parsing
JSON structures. Developers can expose delivered usages of undocumented functions by doing “Find In” searches in App
Designer. This method of uncovering undocumented functions is rather tedious and only exposes the undocumented functions
that are being used in delivered code. It is quite possible that there are delivered undocumented functions that are not
being used in any delivered code. How can we expose these undocumented, unused functions?</p>
<p>The answer is to use Java to perform type introspection on the delivered <code class="language-plaintext highlighter-rouge">PeopleSoft.PeopleCode.Func</code> Java Class. The Func
Java Class is mentioned in the <em>From Java to PeopleCode</em> section of PeopleBooks. This class holds methods that replicate
the built-in PeopleCode functions. I am unsure if this class holds all of the available built-in functions, but I have
discovered that it holds some functions that are undocumented.</p>
<p>I used the <a href="http://jjmpsj.blogspot.com/2016/07/dynamic-java-in-peoplecode.html">Dynamic Java in PeopleCode</a> approach to
run the type introspection Java code via JavaScript. Here is the JavaScript program that performs type introspection on
the <code class="language-plaintext highlighter-rouge">PeopleSoft.PeopleCode.Func</code> Java Class:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">peopleCodeClass</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Packages</span><span class="p">.</span><span class="nx">PeopleSoft</span><span class="p">.</span><span class="nx">PeopleCode</span><span class="p">.</span><span class="nx">Func</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">className</span> <span class="o">=</span> <span class="nx">peopleCodeClass</span><span class="p">.</span><span class="nx">getClass</span><span class="p">().</span><span class="nx">getName</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">classMethods</span> <span class="o">=</span> <span class="nx">peopleCodeClass</span><span class="p">.</span><span class="nx">getClass</span><span class="p">().</span><span class="nx">getMethods</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">methods</span> <span class="o">=</span> <span class="p">[];</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o"><</span> <span class="nx">classMethods</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">method</span> <span class="o">=</span> <span class="p">{};</span>
<span class="nx">method</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="nx">classMethods</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">getName</span><span class="p">();</span>
<span class="nx">method</span><span class="p">.</span><span class="nx">returnType</span> <span class="o">=</span> <span class="nx">classMethods</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">getReturnType</span><span class="p">().</span><span class="nx">getSimpleName</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">parameters</span> <span class="o">=</span> <span class="p">[];</span>
<span class="kd">var</span> <span class="nx">paramType</span> <span class="o">=</span> <span class="nx">classMethods</span><span class="p">[</span><span class="nx">i</span><span class="p">].</span><span class="nx">getParameterTypes</span><span class="p">();</span>
<span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">j</span> <span class="o"><</span> <span class="nx">paramType</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">parameter</span> <span class="o">=</span> <span class="p">{};</span>
<span class="nx">parameter</span><span class="p">.</span><span class="nx">type</span> <span class="o">=</span> <span class="nx">paramType</span><span class="p">[</span><span class="nx">j</span><span class="p">].</span><span class="nx">getSimpleName</span><span class="p">();</span>
<span class="nx">parameters</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">parameter</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">method</span><span class="p">.</span><span class="nx">parameters</span> <span class="o">=</span> <span class="nx">parameters</span><span class="p">;</span>
<span class="nx">methods</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">method</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">var</span> <span class="nx">classDetails</span> <span class="o">=</span> <span class="p">{};</span>
<span class="nx">classDetails</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="nx">className</span><span class="p">;</span>
<span class="nx">classDetails</span><span class="p">.</span><span class="nx">methods</span> <span class="o">=</span> <span class="nx">methods</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">result</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">classDetails</span><span class="p">);</span>
</code></pre></div></div>
<p>The program puts the function information in a JSON string and stores it in the <code class="language-plaintext highlighter-rouge">result</code> JavaScript variable. This
program can be stored in an HTML Object (PSM_TEST in this example) and be invoked from PeopleCode as follows:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joManager</span> <span class="o">=</span> <span class="nc">CreateJavaObject</span><span class="o">(</span><span class="s">"javax.script.ScriptEngineManager"</span><span class="o">);</span>
<span class="nc">Local</span> <span class="nc">JavaObject</span> <span class="o">&</span><span class="n">joEngine</span> <span class="o">=</span> <span class="o">&</span><span class="n">joManager</span><span class="o">.</span><span class="na">getEngineByName</span><span class="o">(</span><span class="s">"JavaScript"</span><span class="o">);</span>
<span class="nc">Local</span> <span class="n">string</span> <span class="o">&</span><span class="n">sProgram</span> <span class="o">=</span> <span class="nc">GetHTMLText</span><span class="o">(</span><span class="no">HTML</span><span class="o">.</span><span class="na">PSM_TEST</span><span class="o">);</span>
<span class="o">&</span><span class="n">joEngine</span><span class="o">.</span><span class="na">eval</span><span class="o">(&</span><span class="n">sProgram</span><span class="o">);</span>
<span class="nc">Local</span> <span class="n">string</span> <span class="o">&</span><span class="n">sResult</span> <span class="o">=</span> <span class="o">&</span><span class="n">joEngine</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"result"</span><span class="o">).</span><span class="na">toString</span><span class="o">();</span>
<span class="nc">Return</span> <span class="o">&</span><span class="n">sResult</span><span class="o">;</span>
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">&sResult</code> PeopleCode variable will hold the function information represented as a JSON string. In my 8.56.12
environment there are 1032 methods listed in the JSON output:</p>
<p><a href="/assets/images/2019/10/Functions.png"><img src="/assets/images/2019/10/Functions.png" alt="Built-In PeopleCode Functions" /></a></p>
<p>You can also use the same approach to expose the available system variables (%UserId, etc.) that PeopleCode has to offer.
To do this, you can replace the first line in the JavaScript program to reference the <code class="language-plaintext highlighter-rouge">PeopleSoft.PeopleCode.SysVar</code> Java
Class:</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">peopleCodeClass</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Packages</span><span class="p">.</span><span class="nx">PeopleSoft</span><span class="p">.</span><span class="nx">PeopleCode</span><span class="p">.</span><span class="nx">SysVar</span><span class="p">();</span>
</code></pre></div></div>
<p>And the output will be similar to the Func Class output:</p>
<p><a href="/assets/images/2019/10/Variables.png"><img src="/assets/images/2019/10/Variables.png" alt="PeopleCode System Variables" /></a></p>
<p>Keep in mind that the type introspection JavaScript program does not perform any data type mappings. You will notice
some Java-specific parameter and return data types listed in the script’s output.
The <em>PeopleCode and Java Data Types Mapping</em> section of PeopleBooks can be referenced to understand the corresponding
PeopleCode types.</p>Colton FischerThere are some really useful built-in PeopleCode functions that are available to be consumed, but are not documented in PeopleBooks. Good examples of these functions include the CreateJsonXxx functions used for creating and parsing JSON structures. Developers can expose delivered usages of undocumented functions by doing “Find In” searches in App Designer. This method of uncovering undocumented functions is rather tedious and only exposes the undocumented functions that are being used in delivered code. It is quite possible that there are delivered undocumented functions that are not being used in any delivered code. How can we expose these undocumented, unused functions?Managing Custom Font Files2019-10-04T06:00:01+00:002019-10-04T06:00:01+00:00http://peoplesoftmods.com/psadmin/managing-custom-font-files<p>Design requirements often require PeopleSoft developers to use custom web fonts when styling PeopleSoft
applications. An easy way to make use of a custom font is to link to a third-party server that hosts the
font files.</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">@font-face</span> <span class="p">{</span>
<span class="nl">font-family</span><span class="p">:</span> <span class="s2">'Mansalva'</span><span class="p">;</span>
<span class="nl">font-style</span><span class="p">:</span> <span class="nb">normal</span><span class="p">;</span>
<span class="nl">src</span><span class="p">:</span> <span class="sx">url(https://fonts.gstatic.com/s/mansalva/v1/aWB4m0aacbtDfvq5NKliKY8.woff2)</span> <span class="n">format</span><span class="p">(</span><span class="s2">'woff2'</span><span class="p">);</span>
<span class="p">}</span>
<span class="nc">.ps-label</span> <span class="p">{</span>
<span class="nl">font-family</span><span class="p">:</span> <span class="s2">'Mansalva'</span><span class="p">,</span> <span class="nb">cursive</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>However, this method causes an undesirable dependency on the third-party server and as well as might not
be an option with local firewall constraints.</p>
<h3 id="an-alternative">An Alternative</h3>
<p>Another option to access custom fonts from PeopleSoft application CSS is to host the custom web font on the
PeopleSoft web server. This requires the web font file(s) to be placed in an accessible directory (typically
the <code class="language-plaintext highlighter-rouge">fonts</code> directory) on the server to be consumed from the application CSS.</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">@font-face</span> <span class="p">{</span>
<span class="nl">font-family</span><span class="p">:</span> <span class="s2">'Mansalva'</span><span class="p">;</span>
<span class="nl">font-style</span><span class="p">:</span> <span class="nb">normal</span><span class="p">;</span>
<span class="nl">src</span><span class="p">:</span> <span class="sx">url(../fonts/aWB4m0aacbtDfvq5NKliKY8.woff2)</span> <span class="n">format</span><span class="p">(</span><span class="s2">'woff2'</span><span class="p">);</span>
<span class="p">}</span>
<span class="nc">.ps-label</span> <span class="p">{</span>
<span class="nl">font-family</span><span class="p">:</span> <span class="s2">'Mansalva'</span><span class="p">,</span> <span class="nb">cursive</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This option works well, but managing custom font files on the web server can be problematic from a lifecycle
management perspective as the fonts are not managed by PeopleTools. The self-hosting of font files requires a
different migration workflow versus the standard App Designer migration. This variation of object management
causes more moving parts and higher chances for mistakes to occur when performing migrations and installations
of the custom application code.</p>
<h3 id="a-solution">A Solution</h3>
<p>A technique to avoid having to manage custom font files on the web server is to store the font files in Image
objects within App Designer. These Image objects can be referenced in the application CSS with the use of
the <code class="language-plaintext highlighter-rouge">%Image</code> meta-function.</p>
<div class="language-css highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">@font-face</span> <span class="p">{</span>
<span class="nl">font-family</span><span class="p">:</span> <span class="s2">'Mansalva'</span><span class="p">;</span>
<span class="nl">font-style</span><span class="p">:</span> <span class="nb">normal</span><span class="p">;</span>
<span class="nl">src</span><span class="p">:</span> <span class="sx">url(%Image(PSM_MY_CUSTOM_FONT)</span><span class="p">)</span> <span class="n">format</span><span class="p">(</span><span class="s2">'woff2'</span><span class="p">);</span>
<span class="p">}</span>
<span class="nc">.ps-label</span> <span class="p">{</span>
<span class="nl">font-family</span><span class="p">:</span> <span class="s2">'Mansalva'</span><span class="p">,</span> <span class="nb">cursive</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">%Image</code> meta-function will resolve to a URL of the font file stored in the web server cache directory:</p>
<p><a href="/assets/images/2019/10/CachedFontFile.png"><img src="/assets/images/2019/10/CachedFontFile.png" alt="Cached Font File" /></a></p>
<p>This technique allows for the custom font file to be served from the web server without having to manually
manage the file on the server. Additionally, this technique allows App Designer to manage/migrate the custom
font to ease lifecycle management.</p>Colton FischerDesign requirements often require PeopleSoft developers to use custom web fonts when styling PeopleSoft applications. An easy way to make use of a custom font is to link to a third-party server that hosts the font files.Fluid Component Logging Techniques2019-10-04T06:00:01+00:002019-10-04T06:00:01+00:00http://peoplesoftmods.com/tips-and-tricks/fluid-component-logging-techniques<p>I find that effective usage of browser developer tools is crucial for productive PeopleSoft
development. Having the ability to inspect and edit the CSS and HTML code directly within the
browser makes for a phenomenal developer experience. A really handy feature of browser dev tools
is the JavaScript console. The JavaScript console allows you to run lines of JavaScript against the
page currently loaded in the browser and reports the errors encountered as the browser tries to
execute your code.</p>
<h3 id="simple-fluid-component-logging">Simple Fluid Component Logging</h3>
<p>For Fluid Component development, we can make use of the <code class="language-plaintext highlighter-rouge">AddOnLoadScript</code> function to execute console
log statements. This can be useful in a Component debugging scenario where you want the ability to see
server-side log statement output in the browser. Here is an example of this technique determining if the
user has the PeopleSoft Administrator Role on the Fluid Homepage Component PeopleCode:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">If</span> <span class="nf">IsUserInRole</span><span class="o">(</span><span class="s">"PeopleSoft Administrator"</span><span class="o">)</span> <span class="nc">Then</span>
<span class="nf">AddOnLoadScript</span><span class="o">(</span><span class="s">"console.log('User Has PS Admin Role');"</span><span class="o">);</span>
<span class="nc">End</span><span class="o">-</span><span class="nc">If</span><span class="o">;</span>
</code></pre></div></div>
<p>And the output in the browser console:</p>
<p><a href="/assets/images/2019/10/AddOnloadScript_Console_Log.png"><img src="/assets/images/2019/10/AddOnloadScript_Console_Log.png" alt="AddOnLoadScript Console Log" /></a></p>
<h3 id="advanced-fluid-component-logging">Advanced Fluid Component Logging</h3>
<p>A nice feature of the browser console is that it provides support for viewing JSON objects by allowing
the developer to expand and collapse the objects and arrays within the JSON structure.</p>
<p><a href="/assets/images/2019/10/JSON_Viewer.png"><img src="/assets/images/2019/10/JSON_Viewer.png" alt="AddOnLoadScript Console Log" /></a></p>
<p>In a Fluid Component debugging context, the interactive JSON viewer can be used to view the Component
buffer data in a structured fashion. The developer just needs to be able to convert PeopleSoft
Component data types (Rowsets, Rows, Records, Fields, etc.) to JSON objects to be logged in the console.</p>
<p>PeopleTools delivers the <code class="language-plaintext highlighter-rouge">JsonBuilder</code> Class that allows developers to build dynamic JSON objects.
The JSON objects can be converted to String and outputted in the browser console
using <code class="language-plaintext highlighter-rouge">AddOnLoadScript</code>.</p>
<p>Here is an example of converting a populated <code class="language-plaintext highlighter-rouge">PSOPRDEFN</code> Record to JSON and
logging it to the console:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Local</span> <span class="nc">Record</span> <span class="o">&</span><span class="n">rOprDefn</span> <span class="o">=</span> <span class="nc">CreateRecord</span><span class="o">(</span><span class="nc">Record</span><span class="o">.</span><span class="na">PSOPRDEFN</span><span class="o">);</span>
<span class="o">&</span><span class="n">rOprDefn</span><span class="o">.</span><span class="na">OPRID</span><span class="o">.</span><span class="na">Value</span> <span class="o">=</span> <span class="o">%</span><span class="nc">OperatorId</span><span class="o">;</span>
<span class="o">&</span><span class="n">rOprDefn</span><span class="o">.</span><span class="na">SelectByKey</span><span class="o">();</span>
<span class="nc">Local</span> <span class="nc">JsonBuilder</span> <span class="o">&</span><span class="n">jbData</span> <span class="o">=</span> <span class="nc">CreateJsonBuilder</span><span class="o">();</span>
<span class="o">&</span><span class="n">jbData</span><span class="o">.</span><span class="na">StartObject</span><span class="o">(&</span><span class="n">rOprDefn</span><span class="o">.</span><span class="na">Name</span><span class="o">);</span>
<span class="nc">Local</span> <span class="n">integer</span> <span class="o">&</span><span class="n">x</span><span class="o">;</span>
<span class="nc">For</span> <span class="o">&</span><span class="n">x</span> <span class="o">=</span> <span class="mi">1</span> <span class="nc">To</span> <span class="o">&</span><span class="n">rOprDefn</span><span class="o">.</span><span class="na">FieldCount</span>
<span class="nc">Local</span> <span class="nc">Field</span> <span class="o">&</span><span class="n">fField</span> <span class="o">=</span> <span class="o">&</span><span class="n">rOprDefn</span><span class="o">.</span><span class="na">GetField</span><span class="o">(&</span><span class="n">x</span><span class="o">);</span>
<span class="o">&</span><span class="n">jbData</span><span class="o">.</span><span class="na">AddProperty</span><span class="o">(&</span><span class="n">fField</span><span class="o">.</span><span class="na">Name</span><span class="o">,</span> <span class="o">&</span><span class="n">fField</span><span class="o">.</span><span class="na">Value</span><span class="o">);</span>
<span class="nc">End</span><span class="o">-</span><span class="nc">For</span><span class="o">;</span>
<span class="o">&</span><span class="n">jbData</span><span class="o">.</span><span class="na">EndObject</span><span class="o">(&</span><span class="n">rOprDefn</span><span class="o">.</span><span class="na">Name</span><span class="o">);</span>
<span class="nc">AddOnLoadScript</span><span class="o">(</span><span class="s">"console.log("</span> <span class="o">|</span> <span class="o">&</span><span class="n">jbData</span><span class="o">.</span><span class="na">ToString</span><span class="o">()</span> <span class="o">|</span> <span class="s">");"</span><span class="o">);</span>
</code></pre></div></div>
<p>I am able to view and expand the JSON representation of the logged Record object in the browser console:</p>
<p><a href="/assets/images/2019/10/Console_Log_PSOPRDEFN.png"><img src="/assets/images/2019/10/Console_Log_PSOPRDEFN.png" alt="AddOnLoadScript Console Log" /></a></p>
<p>In a real world Component debugging scenario, it is not practical to write the several additional
lines of code for the sake of getting debugging information written to the browser console. Instead,
utility Application Classes can be written to transform PeopleSoft data types to JSON objects to
abstract unnecessary logic (loops, etc.) when the need for object conversion arises. The logging
implementation logic (<code class="language-plaintext highlighter-rouge">AddOnLoadScript</code>, etc.) can also be abstracted in an Application Class to
allow for simple invocation of the logging statements.</p>Colton FischerI find that effective usage of browser developer tools is crucial for productive PeopleSoft development. Having the ability to inspect and edit the CSS and HTML code directly within the browser makes for a phenomenal developer experience. A really handy feature of browser dev tools is the JavaScript console. The JavaScript console allows you to run lines of JavaScript against the page currently loaded in the browser and reports the errors encountered as the browser tries to execute your code.