How to install Shinobi in a FreeNAS (11.3-RELEASE) Jail

I've been running Shinobi in a FreeNAS jail for more than 2 years now and although there were some hiccups, they are still manageable. I created a blog post on how to do that as well but that's for a previous version of FreeNAS. FreeNAS has had several updates since then and so did Shinobi so I decided to write up another post on how to setup and install the latest Shinobi (as of writing) to a FreeNAS (11.3-RELEASE) jail.

NOTE: The following setup guide is actually taken from the freebsd.sh and freenas.csh scripts that comes with Shinobi. Unfortunately, running either scripts produces some errors for me and installation fails at some point. So what I did is get the commands that works and I execute them one by one.

Installation and Setup

  1. Login to FreeNAS ui as root and navigate to Jails and add a Jail.
  2. Launch the Shell for that Jail. Must be root.
  3. To download Shinobi, we'll need to install the git application. Run the command below and follow the installation instructions.

    TIP: You can copy the commands here and paste them in FreeNAS shell by pressing Shift + Insert keys on your keyboard.
    pkg install git
  4. Shinobi requires certain software to run properly so we'll need to install them first. Type the following commands and follow the installation instructions to install.
    pkg install ffmpeg
    pkg install x264
    pkg install x265
    pkg install node
    pkg install npm
    pkg install mysql57-server
    
  5. Now we download Shinobi.
    git clone https://gitlab.com/Shinobi-Systems/Shinobi.git Shinobi
  6. Change directory to Shinobi..
    cd Shinobi
    It is also a good idea to know which directory Shinobi was installed.
    pwd
    Take note of this as you might need to change the path on some of my setups later. In this tutorial, I installed Shinobi in /root so you might see me referencing /root/Shinobi. Yours might be different so just replace the path if necessary.

  7. We'll need to do some additional setups for the MySQL server.
    • First of, enable mysql.
      sysrc mysql_enable=YES
    • Then start mysql.
      service mysql-server start
    • Now we need to login to mysql via command line but before we can do that, we need to know the password. After a fresh install of MySQL server, a file with a temporary password is generated. This file is usually located in /home/.mysql_secret. In this case, it should be in /root/.mysql_secret. To view the file, run the command below and make sure to take note of the password.
      cat /root/.mysql_secret
    • Login to MySQL
      mysql -h localhost -u root -p
      It will now ask you to type the password. Once logged in, we need to change the password for security purposes.
      alter user 'root'@'localhost' identified by 'newpassword';
    • Assuming we are still in the mysql command line interface, let's now add Shinobi user to the database.
      source /root/Shinobi/sql/user.sql
      If this work properly, you should see a message similar to "Query OK..."
    • Next, setup Shinobi framework to the database.
      source /root/Shinobi/sql/framework.sql
      This is it for the MySQL setup. Type quit to exit mysql command line interface.
  8. Now on to the Shinobi setups. I've listed the commands below. Just type them one by one.
    npm install -g npm -g
    npm install --unsafe-perm
    npm install pm2@3.0.0 -g
    cd Shinobi
    cp conf.sample.json conf.json
    cp super.sample.json super.json
    pm2 start camera.js
    pm2 start cron.js
    pm2 save
    pm2 list
    pm2 startup rcd

That's it. If everything went well, you should be able to access your Shinobi server through your browser and opening http://your_jail_ip_address:8080. Default username is admin@shinobi.video and password is admin.

P.S. I also developed an android application which you can get at Google Play. It needs a lot of work but if setup correctly, you will be able to view and control your cameras.

A simple way to add a delete row function from a report in Oracle APEX

I had a requirement to add a delete button in an Interactive Report of Oracle APEX. I did some research online and found a few articles discussing on how to do this. But they are using java scripts and dynamic actions.

However, I wanted to see if there is a simpler way to do this. After giving it some more thought, I figured out something simple. Let me show you.

Assuming we had an Interactive Report as shown in the Figure below.



Now we add a column link. We can do this by adding an additional item in the query or using the Interactive Report Attributes. I'll use the first approach in this case. See screen shots below.





At this point, we have an additional column in the report (see screen shot below) but it doesn't do anything yet.



Now on to the solution.


Create a blank page and set it as a Modal Dialog.



Create a Region and set the Type as Static Content under Identification. Set the region's Text field under Source to "Would you like to perform this delete action?". Set the region's Template under Appearance to Blank with Attributes (No Grid).



Add a page item to the region and name it to PX_ID (where X is the page number). Set it to Hidden.



Add a Cancel button in the region and create a Cancel Dialog Dynamic Action to the Cancel button.



Add an OK button in the region.

Create a process with Type of PL/SQL Code. Select the button OK on When Button Pressed under Server-side Condition. Then in the PL/SQL Code under Source, enter the appropriate code to delete the record. In my case, I need to execute the following:
DELETE FROM my_table WHERE id = :PX_ID;



Create a process with Type of Close Dialog. Select the button OK on When Button Pressed under Server-side Condition.



Let's go back to the report page and edit the following properties for the DELETE_LINK column.

Under Link, set Target to Page in this application.

Under Page, select the modal page we just created.

Under Set Items, select the PX_ID (where X is the page number of modal page) for Name and PY_ID (where Y is the page number of report page).

Under Clear Cache, enter the page number of the modal page then click OK.




In the Link Text under Link, let's change this to a trash icon. Enter the following:
<span aria-hidden="true" class="fa fa-trash"></span>


Now all that's left to do is to run it.



That's it. Enjoy!

Setup FreeNAS Dynamic DNS with Namecheap

In my previous post, I discussed about setting up Shinobi on a FreeNAS jail. I also mentioned that in doing this, I will need to port forward my Shinobi jail ip to make it accessible from outside. This way I can view my streams from anywhere.

In this post, I will be writing about how to setup ddns in FreeNAS with Namecheap as the provider. This is recommended if your ISP supplies you a dynamic ip address, which is my case, as opposed to having a static ip address which you can directly assign to a domain or subdomain in your Namecheap Advanced DNS setup page.

There are actually two ways that I know of on how to do this:
  1. Using FreeNAS Dynamic DNS
  2. Using FreeNAS cron job

Using FreeNAS Dynamic DNS


Login to your FreeNAS admin user interface and navigate to Dynamic DNS under Services.


Enter the following information:
FieldValue
  
ProviderCustom Provider
Use SSLChecked
Custom Serverdynamicdns.park-your-domain.com
Custom Path/update?host=%h&domain=[domain]&password=[password]
Domain Namehost
Usernamedomain name
PasswordNamecheap DDNS Password
Confirm PasswordNamecheap DDNS Password
Update Period600


Explanation of fields and values

Provider
This is the DNS provider. However at the time of this writing, Namecheap is not included among the list. So in order to use Namecheap, we use Custom Provider. Fortunately, Namecheap provides a way to update your host's ip address through your browser. This can be done by simply opening the URL and passing the appropriate parameters. You can find more information on this in their Knowledgebase here.
https://dynamicdns.park-your-domain.com/update?host=%h&domain=[domain]&password=[password]
Use SSL
Whether or not Namecheap's custom server is using SSL. Since the URL uses HTTPS, then we check SSL.
Custom Server
This is the hostname in the provided URL earlier.
Custom Path
This is the path along with the required query parameters and values after the custom server hostname in the URL mentioned above. /update?host=%h&domain=[domain]&password=[password]

where:
  • %h is the value of the field Domain Name
  • domain is your domain.tld. For ex: google.com
  • password is you Namecheap DDNS password which can be seen at the Namecheap Advanced DNS page. See sample screen shot below.

Update Period
This is the frequency you want FreeNAS Dynamic DNS service to update your host's ip address in seconds.

Once you've entered all the necessary information, click OK. Make sure the your Dynamic DNS service is running by the way. You can check that by going to the Services tab.


After the update period has passed, go to your Namecheap's Advanced DNS page and check your updated ip address.

Using FreeNAS cron job


As mentioned earlier, Namecheap provides a way to update your host's ip address through your browser.

To use this in a cron job, we simply need to execute a command like curl or wget to access the URL. I'll be using curl in my case. The only difference here is that we specify the host value here as opposed to using %h earlier. For example:
curl "https://dynamicdns.park-your-domain.com/update?host=www&domain=google.com&password=your-namecheap-dns-password-here"
Then specify the preferred schedule. Once the cron job executes, go to your Namecheap's Advanced DNS page and check your updated ip address.

That's it. Enjoy!

References

How to install Shinobi Video on a FreeNAS Jail

Prelude


So I've been doing a lot of research lately on how I should go about setting up surveillance cameras around our home. Features like remote viewing, motion detection and alerts are a necessity. At first I thought of using ip cameras then just use their own application for remote viewing and recording like Mi Home from Xiaomi, Hik-Connect from HikVision and so on. This was okay at first but then I found out that most of these cameras upload data to their corresponding servers. From what I understand, these servers act as relays in order to allow consumers to view live streams from their camera remotely without having the need for static ip addresses, port forwarding as well as DVRs/NVRs.

It does make sense to do this in some way because it does make it easier for consumers. Otherwise consumers will need to request static ip addresses from their respective ISPs or setup a DDNS at home, then setup port forwarding on their routers. Security also needs to be considered since they are opening their network to the internet.

However, the fact that these cameras uploads data to their respective servers made me think twice because of privacy reasons. Since I already have a FreeNAS available at home, I decided to just install a surveillance software on it, setup DDNS and setup port forwarding on my router.

First things first, I need to decide which software to use. My requirements are:
  • Should be free.
  • Should support linux (Ubuntu, CentOS, FreeBSD).
  • Should not require too much RAM and CPU.

I only looked at a few software.
  • Zoneminder. Unfortunately, I had a hard time setting this up on a CentOS7 in Google Compute Engine.
  • Shinobi. Successful on first attempt in Google Compute Engine. Also looks promising.
  • Motion. Didn't get to try it since I already installed Shinobi successfully.

And that's how I came to a decision. Shinobi it is.

Before we begin, here are some information about my FreeNAS.
root@shinobi_1:/ # uname -a                                                     
FreeBSD shinobi_1 11.1-STABLE FreeBSD 11.1-STABLE #0 r321665+9902d126c39(freenas/11.1-stable): Tue Aug 21 12:24:37 EDT 2018     root@nemesis.tn.ixsystems.com:/freenas-11-releng/freenas/_BE/objs/freenas-11-releng/freenas/_BE/os/sys/FreeNAS.amd64  amd64
root@shinobi_1:/ # 
Specs:
Intel(R) Pentium(R) CPU G4600 @ 3.60GHz
8GB RAM
2x2TB HDD 3.5"
1x500GB HDD 2.5"

Installation and Setup


Navigate to Jails and add a Jail.
Launch the Shell for that Jail. Must be root.

In my current FreeNAS version, running the installation produces an error.
Shared object "libdl.so.1" not found,
In order to fix this, I had to make some modifications as suggested in this thread. Basically modified the file /usr/local/etc/pkg/repos/FreeBSD.conf

from
FreeBSD: {
  url: "pkg+http://pkg.FreeBSD.org/freebsd:11:x86:64/latest",
  mirror_type: "srv",
  enabled: yes
}
to
FreeBSD: {
  url: "pkg+http://pkg.FreeBSD.org/freebsd:11:x86:64/release_2",
  mirror_type: "srv",
  enabled: yes
}
Then execute the following commands:
pkg update -f
pkg upgrade -f
Once done, pull the repository.
git clone https://gitlab.com/Shinobi-Systems/Shinobi.git Shinobi
Change directory to Shinobi.
cd Shinobi
Start the installer.
chmod +x INSTALL/freenas.sh && INSTALL/freenas.csh
Instructions on how to access your Shinobi instance will be available at the end of the installation process. By default, the URL for the super user should be:
http://your_jails_ip_address:8080/super
Here is where you create user accounts. To access the Dashboard, go to:
http://your_jails_ip_address:8080
Username and Password for this are those created mentioned above.

Sample screenshot after successful installation

Here is a sample screen shot of the Shinobi setup.


And that's it. Enjoy!

Here are some of the references that helped me achieve this.


UPDATE: I've been using Shinobi for more than a year now and it has been sufficient for my needs. I also developed an android application which you can get at Google Play. It needs a lot of work but if setup correctly, you will be able to view and control your cameras.

Custom Interface PL/SQL to return Employee Information using Oracle E-Business Suite Integrated SOA Gateway

In my previous posts, I wrote about how to create custom interface types using Java as part of the Oracle E-Business Suite Integrated SOA Gateway. In this post, I will be building the service using the same requirement I had in the Application Module Service. Only this time, I will be using PL/SQL.

First thing we'll do is to create our package scripts. One for the specification and one for the body.

Here's the package specification. Filename will be xxhr_soa_pkg.pls
create or replace package APPS.xxhr_soa_pkg as.
/* $Header: $ */
/*#
* This is the a sample custom HR SOA Web Service
* @rep:scope public
* @rep:product per
* @rep:category BUSINESS_ENTITY HR_USER_HOOK
* @rep:displayname HR SOA Web Service
*/

  type person_rec is record ( person_id             number
                            , effective_start_date  date
                            , effective_end_date    date
                            , employee_number       varchar2(30)
                            , marital_status        varchar2(30)
                            , date_of_birth         date );
                            
  type person_tbl is table of person_rec;

/*#
 * This API returns a list of person information.
 * @param p_employee_number Employee Number of the Person
 * @return This API will return person_tbl type.
 * @rep:displayname Get Employees
 * @rep:scope public
 * @rep:lifecycle active
*/
  function get_employees ( p_employee_number in varchar2 ) return person_tbl;
end;
/
Here's the package body and the filename will be xxhr_soa_pkg.plb.
create or replace package body APPS.xxhr_soa_pkg as

  function get_employees ( p_employee_number in varchar2 ) return person_tbl
  is
    l_employees person_tbl;
  begin
    select person_id
         , effective_start_date
         , effective_end_date
         , employee_number
         , marital_status
         , date_of_birth
      bulk
   collect
      into l_employees
      from per_people_x
     where hr_person_type_usage_info.get_user_person_type(sysdate,person_id) like 'Employee%'
       and employee_number = nvl(p_employee_number,employee_number);
       
    return l_employees;
  end;
end;
/
Note that only the package specification requires the appropriate annotations. More information on PL/SQL Annotations here.

Of course, we also need to compile the package scripts into the target database. After that, we need to move these files (the specification script at the least) so that we can generate the iLDT file similar to what we did in the previous posts. See command below.
[las@server ~/soa-plsql]$ $IAS_ORACLE_HOME/perl/bin/perl $FND_TOP/bin/irep_parser.pl -g -v -username=sysadmin per:patch/115/sql:xxhr_soa_pkg.pls:12.0=xxhr_soa_pkg.pls
If everything went well, the expected output should be somewhat similar to this.
[las@server ~/soa-plsql]$ $IAS_ORACLE_HOME/perl/bin/perl $FND_TOP/bin/irep_parser.pl -g -v -username=sysadmin per:patch/115/sql:xxhr_soa_pkg.pls:12.0=xxhr_soa_pkg.pls
# Interface Repository Annotation Processor, 12.0.0

#
# Generating annotation output.
# Processing file 'xxhr_soa_pkg.pls'.
# Using YAPP-based parser.
#  Found a package-level annotation for 'APPS.XXHR_SOA_PKG'.
#  Found a detail-level annotation...
# Found a function named 'GET_EMPLOYEES'.
# Done all files.
[las@server ~/soa-plsql]$
Next, we need to upload the iLDT file to our Integrated SOA repository. We can do this by executing the fndload command with the appropriate lct file as shown below.
[las@server ~/soa-plsql]$ FNDLOAD apps/<apps_password> 0 Y UPLOAD $FND_TOP/patch/115/import/wfirep.lct
Again if successful, expected output should be somewhat similar to this.
[las@server ~/soa-plsql]$ FNDLOAD apps/<apps_password> 0 Y UPLOAD $FND_TOP/patch/115/import/wfirep.lct xxhr_soa_pkg_pls.ildt
Log filename : L6603415.log


Report filename : O6603415.out
[las@server~/soa-plsql]$
Now let’s check the service in the Repository.

Login to your Oracle E-Business Suite instance as SYSADMIN.



Switch to Integrated SOA Gateway responsibility.



Select Integration Repository



Click Search and then search for your service. In this case, we'll search for HR SOA Web Service as Internal Name.

HR SOA Web Service image

Notice here that for PL/SQL Interface Type, you have both SOAP and REST web service available. In this post, I will be focusing on the REST web service. Before we go into that, let's click on the Get_Employees item below to see more information.


Moving on, click on the REST Web Service tab. In here, we will need to set an alias for this service which will be used as part of the URL when invoking it. I will enter hr in this case. Then click Deploy.

Sample screen shot of REST Service Tab

Click on the View WADL link to get more information on how to invoke the service. This will show you an XML description of the service.
<?xml version = '1.0' encoding = 'UTF-8'?>
<application name="XXHR_SOA_PKG" targetNamespace="http://xmlns.oracle.com/apps/per/soaprovider/plsql/rest/hr/" xmlns:tns="http://xmlns.oracle.com/apps/per/soaprovider/plsql/rest/hr/" xmlns="http://wadl.dev.java.net/2009/02" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns1="http://xmlns.oracle.com/apps/per/rest/hr/get_employees/">
   <grammars>
      <include href="http://host:port/webservices/rest/hr/?XSD=GET_EMPLOYEES_SYNCH_TYPEDEF.xsd" xmlns="http://www.w3.org/2001/XMLSchema"/>
   </grammars>
   <resources base="http://host:port/webservices/rest/hr/">
      <resource path="get_employees/">
         <method id="GET_EMPLOYEES" name="POST">
            <request>
               <representation mediaType="application/xml" type="GET_EMPLOYEES_Input"/>
               <representation mediaType="application/json" type="GET_EMPLOYEES_Input"/>
            </request>
            <response>
               <representation mediaType="application/xml" type="GET_EMPLOYEES_Output"/>
               <representation mediaType="application/json" type="GET_EMPLOYEES_Output"/>
            </response>
         </method>
      </resource>
   </resources>
</application>
Get the URL for the xsd and open it on a different browser tab/window for additional information. In this case, the URL is
http://host:port/webservices/rest/hr/?XSD=GET_EMPLOYEES_SYNCH_TYPEDEF.xsd
<?xml version = '1.0' encoding = 'UTF-8'?>
<schema targetNamespace="http://xmlns.oracle.com/apps/per/rest/hr/get_employees/" xmlns="http://www.w3.org/2001/XMLSchema" xmlns:db="http://xmlns.oracle.com/apps/per/rest/hr/get_employees/" elementFormDefault="qualified" xmlns:head="http://xmlns.oracle.com/apps/fnd/rest/header" xmlns:isgf="http://xmlns.oracle.com/isg/servicefault">
   <import namespace="http://xmlns.oracle.com/isg/servicefault" schemaLocation="http://host:port/webservices/rest/hr/?XSD=ISGServiceFault.xsd"/>
   <import namespace="http://xmlns.oracle.com/apps/fnd/rest/header" schemaLocation="http://host:port/webservices/rest/hr/?XSD=RESTHeader.xsd"/>
   <element name="InputParameters">
      <complexType>
         <sequence>
            <element name="P_EMPLOYEE_NUMBER" type="string" db:index="1" db:type="VARCHAR2" minOccurs="0" nillable="true"/>
         </sequence>
      </complexType>
   </element>
   <element name="OutputParameters">
      <complexType>
         <sequence>
            <element name="XXHR_SOA_PKG-24GET_EMPLOYEES" type="db:APPS.XXHR_SOA_PKG_PERX3501705X1X1" db:index="0" db:type="Array" minOccurs="0" nillable="true"/>
         </sequence>
      </complexType>
   </element>
   <complexType name="APPS.XXHR_SOA_PKG_PERX3501705X1X2">
      <sequence>
         <element name="PERSON_ID" type="decimal" db:type="NUMBER" minOccurs="0" nillable="true"/>
         <element name="EFFECTIVE_START_DATE" type="dateTime" db:type="DATE" minOccurs="0" nillable="true"/>
         <element name="EFFECTIVE_END_DATE" type="dateTime" db:type="DATE" minOccurs="0" nillable="true"/>
         <element name="EMPLOYEE_NUMBER" db:type="VARCHAR2" minOccurs="0" nillable="true">
            <simpleType>
               <restriction base="string">
                  <maxLength value="30"/>
               </restriction>
            </simpleType>
         </element>
         <element name="MARITAL_STATUS" db:type="VARCHAR2" minOccurs="0" nillable="true">
            <simpleType>
               <restriction base="string">
                  <maxLength value="30"/>
               </restriction>
            </simpleType>
         </element>
         <element name="DATE_OF_BIRTH" type="dateTime" db:type="DATE" minOccurs="0" nillable="true"/>
      </sequence>
   </complexType>
   <complexType name="APPS.XXHR_SOA_PKG_PERX3501705X1X1">
      <sequence>
         <element name="XXHR_SOA_PKG-24GET_EMPLOYEES_ITEM" type="db:APPS.XXHR_SOA_PKG_PERX3501705X1X2" db:type="Struct" minOccurs="0" maxOccurs="unbounded" nillable="true"/>
      </sequence>
   </complexType>
<element name="GET_EMPLOYEES_Input">
      <complexType>
         <sequence>
            <element ref="head:RESTHeader"/>
            <element ref="db:InputParameters"/>
         </sequence>
      </complexType>
   </element>
   <element name="GET_EMPLOYEES_Output">
      <complexType>
         <choice>
            <element ref="db:OutputParameters"/>
            <element ref="isgf:ISGServiceFault"/>
         </choice>
      </complexType>
   </element>
</schema>

Let's not forget to setup the grants. In my case, I granted it to SYSADMIN user.
Sample screen shot grants setup

Then of course we need to bounce the following services

opmn
oafm

Forgot to mention that PL/SQL Interface Type is always invoke using POST method. And since this is the case, we need to build the request body. We will also use Basic Authentication as our authorization method.

So to put it all together, the URL will be:
http://host:port/webservices/rest/hr/get_employees/
The request body will be:
{
  "GET_EMPLOYEES_Input": {
    "@xmlns": "http://xmlns.oracle.com/apps/per/rest/hr/get_employees/",
    "RESTHeader": {
      "@xmlns": "http://xmlns.oracle.com/apps/fnd/rest/header",
      "Responsibility": "SYSTEM_ADMINISTRATOR",
      "RespApplication": "SYSADMIN",
      "SecurityGroup": "STANDARD",
      "NLSLanguage": "AMERICAN"
    },
    "InputParameters": { "P_EMPLOYEE_NUMBER": "123456" }
  }
}
Here's a sample screen shot invoking the service using Postman and getting a response.

Sample screen shot using Postman

Some pointers that I noticed. Input Parameters seems to be optional. So if you wanted to pass a null value for P_EMPLOYEE_NUMBER, you can do either of the following:
"InputParameters": { "P_EMPLOYEE_NUMBER": "" }
or just simply not include it in the body.
"InputParameters": { }
And that's it. Enjoy!

Custom Interface Application Module Service to return Employee Information using Oracle E-Business Suite Integrated SOA Gateway

In this post, I will create a custom interface using Application Module Service as opposed to the Java Bean Service in my previous post here.

Using Application Module Service, we can take advantage of Oracle Application Framework (OAF) components like View Object (VO), Entity Object (EO) etc.

In this test case that I'm working on, I will be using just a view object and the application module.

Here are the details of the Employees View Object (EmployeesVO).

SQL Query
select person_id
     , effective_start_date
     , effective_end_date
     , employee_number
     , marital_status
     , date_of_birth
  from per_people_x
 where hr_person_type_usage_info.get_user_person_type(sysdate,person_id) like 'Employee%'
   and employee_number = nvl(:p_employee_number,employee_number)


Note that I added a bind variable p_employee_number. We'll use this to filter the results of the query later on.

Here are some more setting for the EmployeesVO. Check the "Generate Java File" and "Accessors" for the View Row Class: EmployeesVORowImpl as shown in the screen shot below. This will generate the necessary Java files which we will use to access the Row of the View Object.


Now we need to create the Application Module and attach the Employees View Object to it as shown in the screen shot below.


Next, we need to create a method getEmployees in the Application Module to fetch the rows. See the code below.

EmployeesAMImpl.java
package oracle.apps.per.sample.server;

import java.util.ArrayList;

import oracle.apps.fnd.framework.server.OAApplicationModuleImpl;

import oracle.apps.per.sample.beans.Employee;
import oracle.apps.per.sample.beans.EmployeeInfo;

import oracle.jbo.RowSetIterator;
// ---------------------------------------------------------------------
// ---    File generated by Oracle ADF Business Components Design Time.
// ---    Custom code may be added to this class.
// ---    Warning: Do not modify method signatures of generated methods.
// ---------------------------------------------------------------------

/**
 * A sample class to demonstrate how Application Module can use the ISG REST framework. This class provides
 * methods to return Employees
 * @rep:scope public
 * @rep:product PER
 * @rep:displayname Employees
 * @rep:category BUSINESS_ENTITY HR_USER_HOOK
 * @rep:category IREP_CLASS_SUBTYPE AM_SERVICES
 */
public class EmployeesAMImpl extends OAApplicationModuleImpl {
    /**This is the default constructor (do not remove)
     */
    public EmployeesAMImpl() {
    }

    public EmployeesVOImpl getEmployeesVO1() {
        return (EmployeesVOImpl)findViewObject("EmployeesVO1");
    }

    /**
     * 
     * This method returns Employee Info
     *
     * @param pEmployeeNumber Employee Number of the Employee
     * @return EmployeeInfo
     * @rep:paraminfo {@rep:innertype oracle.apps.per.sample.beans.EmployeeInfo}
     * @rep:scope public
     * @rep:displayname Get Employees
     * @rep:httpverb get
     */
    public EmployeeInfo getEmployees(String pEmployeeNumber) {
        EmployeeInfo info = new EmployeeInfo();
        ArrayList employees = new ArrayList();
        EmployeesVOImpl vo = getEmployeesVO1();
        
        if (pEmployeeNumber != null) {
            vo.setNamedWhereClauseParam("p_employee_number",pEmployeeNumber);
        }
        
        vo.executeQuery();

        RowSetIterator iter = vo.createRowSetIterator(null);
        iter.reset();

        while (iter.hasNext()) {
            EmployeesVORowImpl row = (EmployeesVORowImpl)iter.next();
            Employee emp = new Employee();
            emp.setPersonId(row.getPersonId().intValue());
            emp.setEffectiveStartdate(row.getEffectiveStartDate().dateValue());
            emp.setEffectiveEndDate(row.getEffectiveEndDate().dateValue());
            emp.setEmployeeNumber(row.getEmployeeNumber());
            emp.setMaritalStatus(row.getMaritalStatus());
            emp.setDateOfBirth(row.getDateOfBirth().dateValue());
            employees.add(emp);
        }
        
        info.setEmployees(employees);

        return info;
    }

}

Explanation

The following code is the java class with the required annotations for Oracle E-Business Suite Integrated SOA Gateway. You can refer here for more information on the required annotations.
/**
 * A sample class to demonstrate how Application Module can use the ISG REST framework. This class provides
 * methods to return Employees
 * @rep:scope public
 * @rep:product PER
 * @rep:displayname Employees
 * @rep:category BUSINESS_ENTITY HR_USER_HOOK
 * @rep:category IREP_CLASS_SUBTYPE AM_SERVICES
 */
public class EmployeesAMImpl extends OAApplicationModuleImpl {}

The following code is the method for getting the EmployeeInfo along with the required annotations. You can see here how we declared an EmployeeInfo, an ArrayList of Employee, how we used the View Object to get the data by iterating thru the rows, creating an Employee in each loop and adding it to the ArrayList. Then set the ArrayList to the EmployeeInfo and finally returning the EmployeeInfo. You will also see here the parameter pEmployeeNumber that was mentioned above. In here, I decided to make the parameter optional. If a value is passed then it returns the specific EmployeeInfo. Otherwise, returns all EmployeeInfo.
    /**
     * 
     * This method returns Employee Info
     *
     * @param pEmployeeNumber Employee Number of the Employee
     * @return EmployeeInfo
     * @rep:paraminfo {@rep:innertype oracle.apps.per.sample.beans.EmployeeInfo}
     * @rep:scope public
     * @rep:displayname Get Employees
     * @rep:httpverb get
     */
    public EmployeeInfo getEmployees(String pEmployeeNumber) {
        EmployeeInfo info = new EmployeeInfo();
        ArrayList employees = new ArrayList();
        EmployeesVOImpl vo = getEmployeesVO1();
        
        if (pEmployeeNumber != null || pEmployeeNumber.length() != 0) {
            vo.setNamedWhereClauseParam("p_employee_number",pEmployeeNumber);
        }
        
        vo.executeQuery();

        RowSetIterator iter = vo.createRowSetIterator(null);
        iter.reset();

        while (iter.hasNext()) {
            EmployeesVORowImpl row = (EmployeesVORowImpl)iter.next();
            Employee emp = new Employee();
            emp.setPersonId(row.getPersonId().intValue());
            emp.setEffectiveStartdate(row.getEffectiveStartDate().dateValue());
            emp.setEffectiveEndDate(row.getEffectiveEndDate().dateValue());
            emp.setEmployeeNumber(row.getEmployeeNumber());
            emp.setMaritalStatus(row.getMaritalStatus());
            emp.setDateOfBirth(row.getDateOfBirth().dateValue());
            employees.add(emp);
        }
        
        iter.closeRowSetIterator();
        info.setEmployees(employees);

        return info;
    }

Some pointers to remember when using this interface type.

Oracle Application Framework has a limited number of records allowed to fetch when querying data through a View Object. If you need to return more than the limit, I would suggestion to use an OraclePreparedStatement instead.

Oracle Integrated SOA Gateway doesn't seem to support Oracle Data Types ie. oracle.jbo.domain.Number, oracle.jbo.domain.Date and probably more. For some reason, I get an error "FND_SOA_SERVICE_EXECUTION_ERR" when invoking the service while using these types. I can invoke the service successfully if I use int and Date.

Oracle Integrated SOA Gateway also doesn't seem to support returning Arrays, ArrayList, List objects. I get the same error "FND_SOA_SERVICE_EXECUTION_ERR" if return these so I ended up creating an Employee class and an EmployeeInfo class with an ArrayList of Employee. Here are the codes below.

Employee.java
package oracle.apps.per.sample.beans;

import java.sql.Date;


public class Employee {

    private int personId;
    private Date effectiveStartdate;
    private Date effectiveEndDate;
    private String employeeNumber;
    private String maritalStatus;
    private Date dateOfBirth;
    
    public Employee() {
    }


    public void setPersonId(int personId) {
        this.personId = personId;
    }

    public Number getPersonId() {
        return personId;
    }

    public void setEffectiveStartdate(Date effectiveStartdate) {
        this.effectiveStartdate = effectiveStartdate;
    }

    public Date getEffectiveStartdate() {
        return effectiveStartdate;
    }

    public void setEffectiveEndDate(Date effectiveEndDate) {
        this.effectiveEndDate = effectiveEndDate;
    }

    public Date getEffectiveEndDate() {
        return effectiveEndDate;
    }

    public void setEmployeeNumber(String employeeNumber) {
        this.employeeNumber = employeeNumber;
    }

    public String getEmployeeNumber() {
        return employeeNumber;
    }

    public void setMaritalStatus(String maritalStatus) {
        this.maritalStatus = maritalStatus;
    }

    public String getMaritalStatus() {
        return maritalStatus;
    }

    public void setDateOfBirth(Date dateOfBirth) {
        this.dateOfBirth = dateOfBirth;
    }

    public Date getDateOfBirth() {
        return dateOfBirth;
    }
    
}
EmployeeInfo.java
package oracle.apps.per.sample.beans;

import java.util.List;

public class EmployeeInfo {

    private List employees;
    
    public EmployeeInfo() {
    }
    
    public void setEmployees(List employees) {
        this.employees = employees;
    }
    
    public List getEmployees() {
        return this.employees;
    }
}

Now we need to compile the java codes and then transfer the java files together with the class files to the appropriate folder in the server. Make sure that JAVA_HOME is set to $IAS_ORACLE_HOME/appsutil/jdk.


Then we generate the iLDT file with the following command:
$IAS_ORACLE_HOME/perl/bin/perl $FND_TOP/bin/irep_parser.pl -g -v -username=sysadmin per:patch/115/java:EmployeesAMImpl.java:12.1.21=$JAVA_TOP/oracle/apps/per/sample/server/EmployeesAMImpl.java

If successful, you should see something similar to the screen shot below.


Then we upload the iLDT to the Repository.

FNDLOAD apps/<apps_password> 0 Y UPLOAD $FND_TOP/patch/115/import/wfirep.lct EmployeesAMImpl_java.ildt

Now let’s check the service in the Repository.

Login to your Oracle E-Business Suite instance as SYSADMIN.



Switch to Integrated SOA Gateway responsibility.



Select Integration Repository



Click Search and then search for your service. In my case, I'll search for Employees.


Click the name of the service to see details of it.


Click the WADL link to get more information on how to invoke the service.


And now we invoke the REST web service. I will be using Postman for this. Since we made the parameter p_employee_number optional in the VO and handled the logic in the AM getEmployees method, we can choose to pass a value or not. To pass a value, we just need to add a query parameter pEmployeeNumber.


If we don't pass a value, then all records are returned.


And that's it. Enjoy!

Additional References

Custom Interface Java Bean Service to return "Hello World" using Oracle E-Business Suite Integrated SOA Gateway

We recently upgraded our Oracle E-Business Suite and along with it was the Integrated SOA Gateway. At work, we usually have requirements for systems integration in which we uses flat files. However, since we already have the Integrated SOA Gateway, we decided to give it a try.

The Integrated SOA Gateway supported a few custom interface types. In this post, I will be working on the Java Bean Service. To test this, I’m going to build a very simple custom interface that returns a string “Hello World”. Later on, I can probably try something more complex. But let's stick with this for now.

Before we proceed, let me give you a bit of information about our instance.

Oracle E-Business Suite Release 12.1.3
Java 1.6.0_21
JDBC Driver 11.2.0.1.0
Database Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
Operating System Linux 3.10.0-229.20.1.el7.x86_64

Without further ado, here's my Java code.

HelloWorld.java
package oracle.apps.per.sample.service;

/**
 * A sample class to demonstrate how Java API can use the ISG REST framework. This class provides
 * methods to return string Hello World
 * @rep:scope public
 * @rep:product PER
 * @rep:displayname Hello World
 * @rep:category BUSINESS_ENTITY HR_USER_HOOK
 * @rep:category IREP_CLASS_SUBTYPE JAVA_BEAN_SERVICES
 */
public class HelloWorld {

    public HelloWorld() {
    }
    
    /**
     * 
     * This method returns a list of direct reports of the requesting user.
     *
     * @return Hello World string
     * @rep:scope public
     * @rep:displayname Get Hello World
     * @rep:httpverb get
     */
    public String getHelloWorld() {
        return "Hello World";
    }
        
}

Take note of the annotations on class level and method level as some of these are required. More information on annotations here.

Next is we need to transfer this java file to the appropriate folder in the server. In my case, I will transfer this to $JAVA_TOP/oracle/apps/per/sample/service.



Now we need to connect to the server via ssh. Set JAVA_HOME to $IAS_ORACLE_HOME/appsutil/jdk if not already set. Lastly, make sure you have the appropriate permissions to execute the following commands:

Compile the java file using the javac command.
javac $JAVA_TOP/oracle/apps/per/sample/service/HelloWorld.java


Generate the iLDT file using the irep_parser.pl script.

[las@server ~]$ $IAS_ORACLE_HOME/perl/bin/perl $FND_TOP/bin/irep_parser.pl -g -v -username=sysadmin per:patch/115/java:HelloWorld.java:12.1=$JAVA_TOP/oracle/apps/per/sample/service/HelloWorld.java
If successful, you should see something similar to the screen shot below.


You should also notice that an Integration Repository loader file (ildt) was created as shown above. This file will contain the metadata for the Java Bean Service object.

IMPORTANT NOTE: For succeeding changes, you need to increment the version when running the irep_parser.pl script. In the example above, I used version 12.1. If I need to make changes to the Java code, I will again need to generated the iLDT file but in doing so, I need to use version 12.2 or 12.1.1 for the changes to take effect.

Moving on, here's the content of the iLDT file for reference.

HelloWorld_java.ildt

# $Header: HelloWorld_java.ildt 12.1 2018/12/04 13:54:23 sysadmin  $

# dbdrv: sql fnd patch/115/sql afdlfsub.sql none none none sqlplus \
# dbdrv:  &phase=dat+10 checkfile:~PROD:~PATH:~FILE &un_apps &pw_apps per \
# dbdrv:  patch/115/java HelloWorld_java.ildt 12.1 FND FNDDLFLD NORMAL \
# dbdrv:  ARU2ILDT 50
LANGUAGE = "US"
LDRCONFIG = "wfirep.lct 120.7"

# Generated by the IRep Parser, 12.0.0

# -- Begin Entity Definitions --

DEFINE IREP_OBJECT
  KEY   OBJECT_NAME              VARCHAR2(430)
  KEY   DEST_TABLE               VARCHAR2(1)
  CTX   OWNER                    VARCHAR2(4000)
  BASE  API_NAME                 VARCHAR2(400)
  BASE  OBJ_TYPE                 VARCHAR2(30)
  BASE  SERVICEABLE              VARCHAR2(1)
  BASE  CLASS_RESOURCE_PATH      VARCHAR2(240)
  BASE  PRODUCT                  VARCHAR2(8)
  BASE  IMP_NAME                 VARCHAR2(400)
  BASE  COMPATABILITY            VARCHAR2(1)
  BASE  SCOPE                    VARCHAR2(30)
  BASE  LIFECYCLE                VARCHAR2(30)
  BASE  SOURCE_FILE_PRODUCT      VARCHAR2(8)
  BASE  SOURCE_FILE_PATH         VARCHAR2(100)
  BASE  SOURCE_FILE_NAME         VARCHAR2(36)
  BASE  SOURCE_FILE_VERSION      VARCHAR2(150)
  BASE  DESCRIPTION              VARCHAR2(32000)
  BASE  STANDARD                 VARCHAR2(30)
  BASE  STANDARD_VERSION         VARCHAR2(30)
  BASE  STANDARD_SPEC            VARCHAR2(240)
  TRANS DISPNAME                 VARCHAR2(240)
  TRANS SHORTDISC                VARCHAR2(2000)
  BASE  TIMESTAMP                VARCHAR2(11)
  BASE  OI_FLAG                  VARCHAR2(1)
  BASE  MAPCODE                  VARCHAR2(255)
  BASE  PARSER_VERSION           VARCHAR2(80)
  BASE  SDO_DEF_CLASS            VARCHAR2(400)
  BASE  SDO_CLASS_NAME           VARCHAR2(400)
  BASE  SDO_IS_FILTER            VARCHAR2(1)
  BASE  SDO_FILTER_REQUIRED      VARCHAR2(1)
  BASE  SDO_IS_EXPRESSION        VARCHAR2(1)
  BASE  SB_INTERFACE_CLASS       VARCHAR2(400)
  BASE  CRAWL_CRAWLABLE          VARCHAR2(1)
  BASE  CRAWL_VISIBILITY_LEVEL   VARCHAR2(8)
  BASE  CRAWL_SEARCH_PLUGIN      VARCHAR2(4000)
  BASE  CRAWL_UI_FUNCTION        VARCHAR2(240)
  BASE  CRAWL_CHANGE_EVENT_NAME  VARCHAR2(4000)
  BASE  CRAWL_CHANGE_NTF         VARCHAR2(1)
  BASE  CRAWL_DRIVING_TABLE      VARCHAR2(30)
  BASE  CRAWL_OBJ_ATTR_0         VARCHAR2(2000)
  BASE  CRAWL_OBJ_ATTR_1         VARCHAR2(2000)
  BASE  CRAWL_OBJ_ATTR_2         VARCHAR2(2000)
  BASE  CRAWL_OBJ_ATTR_3         VARCHAR2(2000)
  BASE  CRAWL_OBJ_ATTR_4         VARCHAR2(2000)
  BASE  CRAWL_OBJ_ATTR_5         VARCHAR2(2000)
  BASE  CRAWL_OBJ_ATTR_6         VARCHAR2(2000)
  BASE  CRAWL_OBJ_ATTR_7         VARCHAR2(2000)
  BASE  CRAWL_OBJ_ATTR_8         VARCHAR2(2000)
  BASE  CRAWL_OBJ_ATTR_9         VARCHAR2(2000)

  DEFINE PARENTS
    KEY   PARENT_NAME            VARCHAR2(430)
  END PARENTS

  DEFINE OBJECT_CATEGORY
    KEY   TYPE                   VARCHAR2(30)
    KEY   CODE                   VARCHAR2(30)
    BASE  SEQUENCE               VARCHAR2(50)
  END OBJECT_CATEGORY

  DEFINE OBJECT_FLEXFIELD
    KEY   APPL_SHORT_CODE        VARCHAR2(50)
    KEY   FLEX_TYPE              VARCHAR2(20)
    KEY   FLEX_CODE              VARCHAR2(400)
    BASE  CONTEXT                VARCHAR2(4000)
    BASE  SEGMENT                VARCHAR2(4000)
    BASE  COMPLEX_TYPE           VARCHAR2(4000)
  END OBJECT_FLEXFIELD

  DEFINE OBJ_CHILD_ANNOTATIONS
    KEY   CHILD_FLAG             VARCHAR2(1)
    KEY   VALUE                  VARCHAR2(500)
  END OBJ_CHILD_ANNOTATIONS

  DEFINE TYPE_MEMBERS
    KEY   SEQUENCE               VARCHAR2(50)
    KEY   INNERTYPE_SEQUENCE     VARCHAR2(50)
    BASE  MEMBER_NAME            VARCHAR2(240)
    BASE  TYPE                   VARCHAR2(430)
    BASE  PRECISION              VARCHAR2(50)
    BASE  SIZE                   VARCHAR2(50)
    BASE  SCALE                  VARCHAR2(50)
    BASE  NULL_ALLOWED           VARCHAR2(1)
    BASE  DESCRIPTION            VARCHAR2(4000)
    BASE  ATTR_SET               VARCHAR2(240)
    BASE  PRIMARY_KEY            VARCHAR2(1)
    BASE  TRANSLATABLE           VARCHAR2(1)
    BASE  COMPOSITE              VARCHAR2(1)
    BASE  DOMAIN_NAME            VARCHAR2(240)
    BASE  MEMBER_TYPE_NAME       VARCHAR2(240)
    BASE  SEARCH_CRITERIA_TYPE   VARCHAR2(30)
    BASE  ATTACHMENT             VARCHAR2(1)
    BASE  MIME_TYPE              VARCHAR2(120)
    BASE  DOMAIN_IMPLEMENTATION  VARCHAR2(400)
    BASE  IS_SORTABLE            VARCHAR2(1)
    BASE  CRAWL_IS_DATE_BASED    VARCHAR2(1)
    BASE  CRAWL_MEMBER_VIS_LVL   VARCHAR2(8)
    BASE  CRAWL_IS_DISPLAYED     VARCHAR2(1)
    BASE  CRAWL_UI_FPARAM_NAME   VARCHAR2(240)
    BASE  CRAWL_INDEXED          VARCHAR2(1)
    BASE  CRAWL_STORED           VARCHAR2(1)
    BASE  CRAWL_IS_SECURE        VARCHAR2(1)
    BASE  CRAWL_IS_TITLE         VARCHAR2(1)
    BASE  CRAWL_WEIGHT           VARCHAR2(50)
    BASE  CRAWL_MBR_ATTR_0       VARCHAR2(2000)
    BASE  CRAWL_MBR_ATTR_1       VARCHAR2(2000)
    BASE  CRAWL_MBR_ATTR_2       VARCHAR2(2000)
    BASE  CRAWL_MBR_ATTR_3       VARCHAR2(2000)
    BASE  CRAWL_MBR_ATTR_4       VARCHAR2(2000)
    BASE  CRAWL_MBR_ATTR_5       VARCHAR2(2000)
    BASE  CRAWL_MBR_ATTR_6       VARCHAR2(2000)
    BASE  CRAWL_MBR_ATTR_7       VARCHAR2(2000)
    BASE  CRAWL_MBR_ATTR_8       VARCHAR2(2000)
    BASE  CRAWL_MBR_ATTR_9       VARCHAR2(2000)
  END TYPE_MEMBERS

  DEFINE USES_TABLE
    KEY   TABLE_NAME             VARCHAR2(30)
    BASE  UT_SEQ                 VARCHAR2(50)
    BASE  UT_DIRECTION           VARCHAR2(1)
  END USES_TABLE

  DEFINE USES_MAP
    KEY   MAP_NAME               VARCHAR2(30)
    BASE  UM_SEQ                 VARCHAR2(50)
  END USES_MAP

  DEFINE CLASS_DATASOURCES
    KEY   DATASOURCE_NAME        VARCHAR2(120)
    BASE  DEF_CLASS              VARCHAR2(400)
    BASE  QUERYABLE              VARCHAR2(1)
    BASE  UPDATEABLE             VARCHAR2(1)
    BASE  INSERTABLE             VARCHAR2(1)
    BASE  MERGEABLE              VARCHAR2(1)
    BASE  DELETEABLE             VARCHAR2(1)
    BASE  PROCESS_QNAME          VARCHAR2(120)
    BASE  QUERY_QNAME            VARCHAR2(120)
  END CLASS_DATASOURCES

  DEFINE OBJ_KEY_SET
    KEY   KEY_SET_NAME           VARCHAR2(120)
    KEY   KEY_SET_SEQUENCE       VARCHAR2(50)
    BASE  KEY1_MBR_NAME          VARCHAR2(240)
    BASE  KEY2_MBR_NAME          VARCHAR2(240)
    BASE  KEY3_MBR_NAME          VARCHAR2(240)
    BASE  KEY4_MBR_NAME          VARCHAR2(240)
    BASE  KEY5_MBR_NAME          VARCHAR2(240)
    BASE  ALT1_MBR_NAME          VARCHAR2(240)
    BASE  ALT2_MBR_NAME          VARCHAR2(240)
    BASE  ALT3_MBR_NAME          VARCHAR2(240)
    BASE  ALT4_MBR_NAME          VARCHAR2(240)
    BASE  ALT5_MBR_NAME          VARCHAR2(240)
  END OBJ_KEY_SET

  DEFINE IREP_RECORD
    KEY   COMPLEX_TYPE_NAME      VARCHAR2(480)
    BASE  RECORD_NAME            VARCHAR2(80)
    BASE  SCOPE                  VARCHAR2(30)
    BASE  TYPE                   VARCHAR2(20)
    BASE  LIFECYCLE              VARCHAR2(30)
    BASE  DESCRIPTION            VARCHAR2(32000)
    TRANS USER_RECORD_NAME       VARCHAR2(80)
    TRANS SHORT_DESCRIPTION      VARCHAR2(240)

    DEFINE FIELDS
      KEY   SEQUENCE             VARCHAR2(50)
      KEY   INNERTYPE_SEQUENCE   VARCHAR2(50)
      BASE  NAME                 VARCHAR2(240)
      BASE  DIRECTION            VARCHAR2(1)
      BASE  OPTIONAL             VARCHAR2(1)
      BASE  TYPE                 VARCHAR2(430)
      BASE  PRECISION            VARCHAR2(50)
      BASE  SIZE                 VARCHAR2(50)
      BASE  SCALE                VARCHAR2(50)
      BASE  NULL_ALLOWED         VARCHAR2(1)
      BASE  DESCRIPTION          VARCHAR2(4000)
      BASE  DEFAULT_VALUE        VARCHAR2(255)
      BASE  DISPLAYED            VARCHAR2(1)
      BASE  ATTRIBUTE_SET        VARCHAR2(240)
      BASE  KEY_PARAM            VARCHAR2(1)
    END FIELDS

  END IREP_RECORD

  DEFINE IREP_METHOD
    KEY   FUNCTION_NAME          VARCHAR2(480)
    BASE  METHOD_NAME            VARCHAR2(80)
    KEY   OVERLOAD_SEQ           VARCHAR2(50)
    BASE  SCOPE                  VARCHAR2(30)
    BASE  INPUT_XSD_FILE         VARCHAR2(400)
    BASE  INPUT_ROOT_ELEMENT     VARCHAR2(400)
    BASE  OUTPUT_XSD_FILE        VARCHAR2(400)
    BASE  OUTPUT_ROOT_ELEMENT    VARCHAR2(400)
    BASE  STATIC                 VARCHAR2(1)
    BASE  EXCEPTIONLIST          VARCHAR2(4000)
    BASE  HTTP_VERB              VARCHAR2(50)
    BASE  METHOD_RESOURCE_PATH   VARCHAR2(240)
    BASE  LIFECYCLE              VARCHAR2(30)
    BASE  DESCRIPTION            VARCHAR2(32000)
    BASE  COMPATABILITY          VARCHAR2(1)
    BASE  SYNCHRO                VARCHAR2(1)
    BASE  DIRECTION              VARCHAR2(1)
    BASE  CTX_DEPENDENCE         VARCHAR2(8)
    TRANS USER_FN_NAME           VARCHAR2(80)
    TRANS SHORT_DESCRIPTION      VARCHAR2(240)
    BASE  PRIMARY_FLAG           VARCHAR2(1)
    BASE  INDIRECT_OP_FLAG       VARCHAR2(1)

    DEFINE METHOD_CATEGORY
      KEY   TYPE                 VARCHAR2(30)
      KEY   CODE                 VARCHAR2(30)
      BASE  SEQUENCE             VARCHAR2(50)
    END METHOD_CATEGORY

    DEFINE METHOD_FLEXFIELD
      KEY   APPL_SHORT_CODE      VARCHAR2(50)
      KEY   FLEX_TYPE            VARCHAR2(20)
      KEY   FLEX_CODE            VARCHAR2(400)
      BASE  CONTEXT              VARCHAR2(4000)
      BASE  SEGMENT              VARCHAR2(4000)
      BASE  COMPLEX_TYPE         VARCHAR2(4000)
    END METHOD_FLEXFIELD

    DEFINE METHOD_CHILD_ANNOTATIONS
      KEY   CHILD_FLAG           VARCHAR2(1)
      KEY   VALUE                VARCHAR2(500)
    END METHOD_CHILD_ANNOTATIONS

    DEFINE PARAMS
      KEY   SEQUENCE             VARCHAR2(50)
      KEY   INNERTYPE_SEQUENCE   VARCHAR2(50)
      BASE  NAME                 VARCHAR2(240)
      BASE  DIRECTION            VARCHAR2(1)
      BASE  OPTIONAL             VARCHAR2(1)
      BASE  TYPE                 VARCHAR2(430)
      BASE  PRECISION            VARCHAR2(50)
      BASE  SIZE                 VARCHAR2(50)
      BASE  SCALE                VARCHAR2(50)
      BASE  NULL_ALLOWED         VARCHAR2(1)
      BASE  DESCRIPTION          VARCHAR2(4000)
      BASE  DEFAULT_VALUE        VARCHAR2(255)
      BASE  DISPLAYED            VARCHAR2(1)
      BASE  ATTRIBUTE_SET        VARCHAR2(240)
      BASE  KEY_PARAM            VARCHAR2(1)
    END PARAMS

  END IREP_METHOD

END IREP_OBJECT

# -- End Entity Definitions --

BEGIN IREP_OBJECT "JAVA:oracle.apps.per.sample.service.HelloWorld" "C"
  OWNER = "sysadmin"
  API_NAME = "oracle.apps.per.sample.service.HelloWorld"
  OBJ_TYPE = "JAVA"
  PRODUCT = "PER"
  SCOPE = "PUBLIC"
  SOURCE_FILE_PRODUCT = "per"
  SOURCE_FILE_PATH = "patch/115/java"
  SOURCE_FILE_NAME = "HelloWorld.java"
  SOURCE_FILE_VERSION = "12.1"
  DESCRIPTION = "A sample class to demonstrate how Java API can use the ISG REST framework. This class provides\n\
 methods to return string Hello World"
  DISPNAME = "Hello World"
  SHORTDISC = "A sample class to demonstrate how Java API can use the ISG REST framework."
  TIMESTAMP = "2018/12/04"
  OI_FLAG = "N"
  PARSER_VERSION = "12.0.0"
  SDO_IS_FILTER = "N"
  SDO_FILTER_REQUIRED = "N"
  SDO_IS_EXPRESSION = "N"
  CRAWL_CRAWLABLE = "N"
  CRAWL_CHANGE_NTF = "N"

  BEGIN OBJECT_CATEGORY "BUSINESS_ENTITY" "HR_USER_HOOK"
  END OBJECT_CATEGORY

  BEGIN OBJECT_CATEGORY "IREP_CLASS_SUBTYPE" "JAVA_BEAN_SERVICES"
  END OBJECT_CATEGORY

  BEGIN IREP_METHOD "JAVA:oracle.apps.per.sample.service.HelloWorld:getHelloWorld" "1"
    METHOD_NAME = "getHelloWorld"
    SCOPE = "PUBLIC"
    HTTP_VERB = "GET"
    DESCRIPTION = "This method returns a list of direct reports of the requesting user."
    USER_FN_NAME = "Get Hello World"
    SHORT_DESCRIPTION = "This method returns a list of direct reports of the requesting user."
    PRIMARY_FLAG = "N"
    INDIRECT_OP_FLAG = "N"

    BEGIN METHOD_CATEGORY "BUSINESS_ENTITY" "HR_USER_HOOK"
    END METHOD_CATEGORY

    BEGIN METHOD_CATEGORY "IREP_CLASS_SUBTYPE" "JAVA_BEAN_SERVICES"
    END METHOD_CATEGORY

    BEGIN PARAMS "0" "0"
      NAME = "RETURN"
      DIRECTION = "O"
      OPTIONAL = "N"
      TYPE = "java.lang.String"
      NULL_ALLOWED = "N"
      DESCRIPTION = "Hello World string"
      DISPLAYED = "Y"
    END PARAMS

  END IREP_METHOD

END IREP_OBJECT

Next, we’ll be uploading this file to the Integration Repository using the following command:

FNDLOAD apps/<apps_password> 0 Y UPLOAD $FND_TOP/patch/115/import/wfirep.lct HelloWorld_java.ildt
If successful, you will see something like this:



Let’s check the log file to make sure there were no errors during the upload.



Now let’s check the service in the Repository.

Login to your Oracle E-Business Suite instance as SYSADMIN.



Switch to Integrated SOA Gateway responsibility.



Select Integration Repository



Click Search



Search for “Hello World” in the Interface Name and click Go



Click the Name of the Interface



You should now see the details of the Interface object.



To enable the REST Web Service, click on the REST Web Service tab. Enter an alias for this. Then click Deploy. The alias will be used as part of the URL later on when invoking the service. Once deployed, you can get more information on the service by clicking on the WADL.



Let’s move on to the Grants tab and grant the service to some users. Check the Get Hello World and click Create Grant.



Enter the necessary information then click Create Grant. In my case, I chose Special User for Grantee Type and SYSADMIN for Grantee Name.



At this point, we need to restart the following:

opmn
oafm

You can refer to this link for details on how to start and stop Oracle scripts.

To invoke the web service, I'm going to use Postman as my REST client. Below are the details for the request:
  • Method: GET
  • Url: http://:/webservices/rest/hw/getHelloWorld/?ctx_responsibility=SYSTEM_ADMINISTRATOR&ctx_respapplication=SYSADMIN&ctx_securitygroup=STANDARD&ctx_nlslanguage=AMERICAN
    where:
    • ctx_responsibility. FND Responsibility Key
    • ctx_respapplication. FND Application Short Name
    • ctx_securitygroup. Security Group
    • ctx_nlslanguage. NLS Language
    More information on these here.
  • Authorization: Basic <encoded username:password>
  • Content-Type: application/json

See sample response below:
{
    "OutputParameters": {
        "ControlBean": [
            {
                "fields": "",
                "filter": "",
                "limit": "",
                "offset": "",
                "sort": ""
            }
        ],
        "value": [
            "Hello World"
        ]
    }
}

See screen shot below for sample



And that's it. Enjoy.

References: