This article is presented in tutorial format and can be downloaded from here and sample application from here.
Archive for the 'ADF' Category
Sample application can be found here.
ADF BC REST Services–III – Using Row Finder
Published December 29, 2015 ADF 8 CommentsTags: ADF, REST
In this post, we will see how to use Row Finder in ADF BC REST services.
Open REST resource VO and create a View Criteria as shown below having 2 bind variables for Department name and Location ID.
Go to Row Finders section and create new one with searhByDeptName.
Here we can observe that above VC is selected by default. In Variables section as shown below, we can also set whether bind variable is allowed to be passed in REST resource URL along with Required settings.
Deploy the application and use any REST client to test GET method using the following urls. Observe the usage of row finder and bind variables.
Passing single bind variable:
http://localhost:7001/departmentApi/rest/r1/departments?finder=searchByDeptName;bindDeptName=A
Passing both bind variables:
Now mark bindLocId variable as required and try to test without using it in the URL and you will observe the error as shown below.
ADF BC REST Services–II – Change Indicator
Published November 23, 2015 ADF 1 CommentTags: ADF, REST
In ADF, often we see error saying ‘JBO-25014: Another user has changed the row with primary key oracle.jbo.Key’. The framework throws this error to make sure that none of the user changes are accidentally overwritten by another user and generally occurs when a user trying to modify record that has been just modified and committed by another user. In this post, we will see how to take care of this scenario using ADF BC REST services in context of HTTP PATCH.
ADF BC REST Services make use of attribute called changeIndicator which can be observed in response of GET.
Follow the steps mentioned below to enable this in a resource:
- EO should have an attribute marked as Change Indicator and set Track Change History as shown below.
- Add this attribute in VO i.e to be exposed as REST resource.
Deploy your changes and issue GET to observe changeIndicator as below.
for e.g. changeIndicator for department 10 (resource instance):
ACED0005737200136A6176612E7574696C2E41727261794C6973747881D21D99C7619D0300014900047
3697A65787000000001770400000001737200116A6176612E6C616E672E496E746567657212E2A0A4F7
81873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08
B02000078700000000178
Now update department 10 using following sql query to simulate the actual update.
update departments set department_name = ‘Administration-modified’, object_version_number = object_version_number+1
where department_id = 10
Issue GET again on same resource and observe the changeIndicator.
ACED0005737200136A6176612E7574696C2E41727261794C6973747881D21D99C7619D0300014900047
3697A65787000000001770400000001737200116A6176612E6C616E672E496E746567657212E2A0A4F7
81873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08
B02000078700000000378
As observed above, the value of changeIndicator changes with each update and is calculated by RESTServlet registered in web.xml of RESTWebService project.
Here is an interesting observation and do issue issuing GET for department 10.
http://localhost:7001/departmentApi/rest/r1/departments/10
If we observe HTTP response headers, the value of ETag is same as that of changeIndicator. Hence changeIndicator works in similar lines of ETag defined in HTTP specification.
Now let us observe the behavior of REST resource when ETag is used for If-Match/If-None-Match HTTP headers during GET and PATCH. Basically these HTTP headers tells server to do requested operation when sent Etag value matches or did not match respectively.
Make sure you enclose ETag value with “ (double quotes) as shown below.
If-None-Match:
Using GET:
- When resource is not modified, returns status code as 304.
- When resource is modified, returns response with new changeIndicator value.
Using PATCH:
- When resource is not modified, returns response status code as 412.
- When the resource is not modified, then returns response with new changeIndicator value after update.
If-None-Match |
Modified |
Not Modified |
GET |
Status:200 (Query Successful) |
Status: 304 |
PATCH |
Status: 200 (Update Successful) |
Status: 412 |
If-Match:
Using GET:
- When resource is not modified, resource is returned.
- When resource is modified, expected response status code is 304 but shows 200 with junk response.
Using PATCH:
- When resource is not modified, returns response with new change Indicator value after update.
- When resource is modified, expected response status code is 412 but shows 200 with junk response. However you will observe that the actual update is not happening though it returns 200.
If-Match |
Modified |
Not Modified |
GET |
Status:304 | Status:200 (Query Successful) |
PATCH |
Status:412 | Status: 200 (Update Successful) |
Note: As you observed above, ETag combination with If-Match header is not working as expected which is a bug in this release.
References:
In this blog post, We will see how to expose ADF VOs as REST resources. ADF has got native REST support in 12.2.1 release.
We will use Department, Employee VOs and following AM Data Model here.
Creating Release Version:
Creating a release version in adf-config.xml is the first step to be done before exposing any of the AM VOs as resource. Use the following steps to create one and you can follow your own conventions for versioning REST resources. Here I have given the initial version as r1.
Expose VO as REST resources:
Open AM and navigate to Web Service –> REST and Click + icon.
Creation of REST resources create a new project RESTWebService.jpr in our workspace that can be deployed as WAR through which these REST services get deployed.
Give the resource name as shown below and click OK.
Observe the new RESTWebService project gets created.
Also observe other files related to REST resources that get created as shown below.
You can use the following tabs to choose the methods to be exposed and the attributes to be exposed to consumers.
When a VO has View Links the Resource Structure will show all these VOs as shown below. Check these VOs as shown below if it has to be exposed as child resource.
Deployment:
Modify context root of RESTWebService project as shown below representing the purpose of your REST API.
Optionally, we can modify URL pattern in web.xml as shown below.
Integrated WLS:
Select RESTWebservice project and do Run on right click as shown below.
Standalone WLS:
Create EAR profile for ADF application and include RESTWebService project as shown below and deploy this EAR to standalone WLS.
Once the deployment is done, you can access the REST resource using url like:
http://<<host>>:<<port>>/<<ContextRoot>>/<<url pattern>>/<<version>>/<<resource name>>
For e.g.: http://localhost:7001/departmentApi/rest/r1/departments
We can also use latest keyword to access the latest version of the resource.
For e.g.: http://localhost:7001/departmentApi/rest/latest/departments
You can use any REST client to try out POST, DELETE, PUT, PATCH depending on the operations you exposed on REST resource.
Describing Resource – GET:
URI: http://localhost:7001/departmentApi/rest/r1/departments/describe
Describing Resource Instance – GET:
URI: http://localhost:7001/departmentApi/rest/r1/departments/10/describe
Querying Departments – GET:
URI: http://localhost:7001/departmentApi/rest/r1/departments
Querying a particular Department – GET:
URI: http://localhost:7001/departmentApi/rest/r1/departments/{id}
Creating Department – POST:
URI: http://localhost:7001/departmentApi/rest/r1/departments
Content-Type: application/vnd.oracle.adf.resourceitem+json
Body:
{
“DepartmentId”: 1000,
“DepartmentName”: “Administration”,
“ManagerId”: 200,
“LocationId”: 1700
}
Deleting a Department – DELETE:
URI: http://localhost:7001/departmentApi/rest/r1/departments/{id}
Updating a Department – POST:
URI: http://localhost:7001/departmentApi/rest/r1/departments/{id}
Content-Type: application/vnd.oracle.adf.resourceitem+json
X-HTTP-Method-Override: PATCH
Body: (contains only fields to be modified)
{
“DepartmentName”: “Administration-Modified”
}
Replacing a Department – PUT:
URI: http://localhost:7001/departmentApi/rest/r1/departments/{id}
Content-Type: application/vnd.oracle.adf.resourceitem+json
Body: (Values not sent in body will be set to null)
{
“DepartmentId”:10,
“DepartmentName”: “Administration-Replace”,
“ManagerId”: 100
}
Querying Department for a few fields – GET:
URI: http://localhost:7001/departmentApi/rest/r1/departments?fields=DepartmentName,ManagerId
Querying a Department using an attribute – GET:
URI: http://localhost:7001/departmentApi/rest/r1/departments/{id}?q=DepartmentName=Administration
Querying a Department for only Data – GET:
URI: http://localhost:7001/departmentApi/rest/r1/departments?onlyData=true
Will not fetch any links or metadata for resource instances in response.
Sorting Departments – GET:
URI: http://localhost:7001/departmentApi/rest/r1/departments?orderBy=DepartmentName:asc
URI: http://localhost:7001/departmentApi/rest/r1/departments?orderBy=DepartmentName: desc
Limiting the records in Querying Departments – GET:
URI: http://localhost:7001/departmentApi/rest/r1/departments?limit=2
Fetches only 2 records.
Querying Departments from a particular record– GET:
URI: http://localhost:7001/departmentApi/rest/r1/departments?offset=2
Fetches only 2 records.
URI: http://localhost:7001/departmentApi/rest/r1/departments?offset=2&limit=5
Fetches 5 records starting from 2nd record.
Expanding a Child Resource – GET:
URI: http://localhost:7001/departmentApi/rest/r1/departments?expand=Employee (Child Resource Name)
Querying Child Resource – GET:
URI: http://localhost:7001/departmentApi/rest/r1/departments/{id}/child/Employee
Querying a particular Child Resource – GET:
URI: http://localhost:7001/departmentApi/rest/r1/departments/{id}/child/Employee/{Child Resource Id}
Querying a Child Resource using an attribute – GET:
URI: http://localhost:7001/departmentApi/rest/r1/departments/{id}/child/Employee?q=FirstName=Jennifer
References:
Using IN clause is very common requirement when working with the ADF View Objects and rows have to be filtered on multiple values of the same attribute.
In this blog, Jobinesh shows a way to achieve this requirement using the bind variable. The same approach is not working when we have to use the multiple columns in IN clause like below:
select * from tableA where (col1,col2,col3) IN ()
We can use the following code to achieve this and have tested in ADF 11g.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import oracle.jbo.domain.Array; | |
import oracle.jbo.domain.DomainContext; | |
import oracle.sql.ArrayDescriptor; | |
import oracle.sql.STRUCT; | |
import oracle.sql.StructDescriptor; | |
import java.sql.Connection; | |
import java.sql.PreparedStatement; | |
import java.sql.SQLException; | |
import java.util.ArrayList; | |
import java.util.HashMap; | |
import java.util.List; | |
/* create the DB object and table as below | |
create or replace TYPE DB_OBJECT AS OBJECT( | |
destcol1 varchar2(100 char), destcol2 number,destcol3 varchar2(100 char),destcol4 number ); | |
create or replace TYPE DB_TABLE AS TABLE OF DB_OBJECT; | |
*/ | |
public void setVOArrayBindVariable(){ | |
Connection conn = null; | |
PreparedStatement stmt = null; | |
StructDescriptor structDescriptor = null; | |
ArrayList<STRUCT> arrayList = new ArrayList<STRUCT>(); | |
try{ | |
stmt = getDBTransaction().createPreparedStatement("select 1 from dual", 0); | |
conn = stmt.getConnection(); | |
structDescriptor = StructDescriptor.createDescriptor("DB_OBJECT",conn); | |
//loop through and populate your arrayList with the values to be used in IN clause | |
for (int j = 0; j < 10; j++) { | |
// constucting the Object to create struct definition | |
Object[] o = new Object[]{getValue1(), getValue2(),getValue3(),getValue4()}; | |
STRUCT item = new STRUCT(structDescriptor,conn,o); | |
conn.createStruct("DB_OBJECT", o); | |
arrayList.add(eventRowStruct); | |
} | |
if(eventDataIdList.size() > 0){ | |
ArrayDescriptor tableArray = ArrayDescriptor.createDescriptor("DB_TABLE", conn); | |
Array domainArray = new Array(tableArray,conn,arrayList.toArray()); | |
//set the bind variable. your VO should have bind variable of type oracle.jbo.domain.Array | |
vo.setbindEventDetailArray(domainArray); | |
//execute the query | |
vo.executeQuery(); | |
} | |
} catch (SQLException sqlex) { | |
//do log the error | |
} catch (Exception ex) { | |
//do log the error | |
} finally { | |
try { | |
stmt.close(); | |
} catch (SQLException sqlex) { | |
//do log the error | |
} | |
} | |
} | |
/* your getCriteriaItemClause() code look like below */ | |
public String getCriteriaItemClause(){ | |
//do the following only for your VC and VC item. details are omitted here | |
String whereClause = "(1=1)"; | |
String eoName = this.getEntityDef(0).getAliasName(); | |
if(getbindDomainArray() != null){ | |
whereCluase = | |
" (" +eoName + ".col1,"+eoName + ".col2,"+eoName + ".col3,"+ eoName+".col4)" + | |
" IN (SELECT destcol1, destcol2, destcol3, destcol4 FROM TABLE(:bindDomainArray))"; | |
} | |
return whereClause; | |
} |
How-To: Attach OWSM Policy to ADF BC Service
Published August 8, 2013 ADF Leave a CommentTags: ADF BC Service, OWSM, Policy, Security
Open ServiceAMImpl and place the cursor on the Annotations section.
Go to ‘Web Services Extension’ in the property inspector and click on the LOV for Security that brings up the all available OWSM policies.
Select any service policy from the list based on your requirements and click on OK.
Observe that ServiceImpl is updated showing new annotation with the selected OWSM policy.
How-To: Populate PL/SQL Collections in ADF Code
Published August 5, 2013 ADF 3 CommentsTags: ADF, JDBC, PL/SQL
People tend to write lot of business logic in PL/SQL when there is lot of data to manipulate with or to improve the performance. When PL/SQL collections are the best way to accept multiple records as input and return the response.
This post shows the sample code to call a PL/SQL API accepting PL/SQL table from an ADF application module.
PL/SQL Objects:
CREATE OR REPLACE TYPE emp_rec_type AS OBJECT (
employee_id NUMBER(18),
first_name VARCHAR2(30),
last_name VARCHAR2(30),
middle_name VARCHAR2(30),
employee_type VARCHAR2(30),
location VARCHAR2(30),
addr_line1 VARCHAR2(30),
addr_line2 VARCHAR2(30),
addr_line3 VARCHAR2(30)
city VARCHAR2(30)
);
CREATE OR REPLACE TYPE emp_tbl_type AS TABLE OF emp_record;
CREATE OR REPLACE PACKAGE emp_ddl_package AS
PROCEDURE validate_emp_records (
emp_rec_list IN OUT NOCOPY emp_tbl_type,
validation_status OUT NOCOPY VARCHAR2
);
END emp_ddl_package;
CREATE OR REPLACE PACKAGE BODY emp_ddl_package AS
PROCEDURE validate_emp_records(
emp_rec_list IN OUT NOCOPY emp_tbl_type,
validation_status OUT NOCOPY VARCHAR2
)IS
BEGIN
— do the coding here
END validate_emp_records;
END emp_ddl_package;
Sample Code of Java Method in Application Module:
Following is the typical piece of code that can be used in ADF application module to call the PL/SQL API:
public List<EmpVORowImpl> validateEmployees(List<EmpVORowImpl> empList) throws Exception {
OracleCallableStatement stmt = null;
ArrayList<EmpVORowImpl> empRespList = null;
if (empList != null && empList.size() > 0) {
empRespList = new ArrayList<EmpVORowImpl>();
try {
// Call the pl/sql API to determine invoice status
String sql =
"begin emp_ddl_package.validate_emp_records(emp_rec_list=>:1,validation_status=>:2); end;";
stmt =
(OracleCallableStatement)getDBTransaction().createCallableStatement(sql,0);
ArrayList<STRUCT> inputEmpList = new ArrayList<STRUCT>();
StructDescriptor structDescriptor = StructDescriptor.createDescriptor(“EMP_REC_TYPE”,stmt.getConnection());
for (int i = 0; i < empList.size(); i++) {
EmpVORowImpl empRow = empList.get(i);
Object[] o =
new Object[] { empRow.getEmployeeId(), empRow.getFirstName(),
empRow.getLastName(), empRow.getMiddleName(),
empRow.getEmployeeType(), empRow.getLocation(),
empRow.getAddrLine1(), empRow.getAddrLine2(),
empRow.getAddrLine3(), empRow.getCity() };
STRUCT empRowStruct = new STRUCT(structDescriptor,stmt.getConnection(), o);
inputEmpList.add(empRowStruct);
}
ArrayDescriptor empArray =
ArrayDescriptor.createDescriptor("EMP_TBL_TYPE",
stmt.getConnection());
ARRAY empArrayTbl =
new ARRAY(empArray, stmt.getConnection(), inputEmpList.toArray());
stmt.registerOutParameter(1, OracleTypes.ARRAY, "EMP_TBL_TYPE");
stmt.setARRAY(1, empArrayTbl);
stmt.registerOutParameter(2, OracleTypes.VARCHAR, 0, 1);
stmt.execute();
//get the output table from pl/sql API and return
Array outputArray = stmt.getArray(1);
String returnStatus = stmt.getString(2);
if (returnStatus != null && "Success".equals(returnStatus) &&
outputArray != null) {
ResultSet rs = outputArray.getResultSet();
EmpVORowImpl tempRow;
while (rs != null && rs.next()) {
Struct empRespStruct = (Struct)rs.getObject(2);
Object[] empAttributes = empRespStruct.getAttributes();
if (empAttributes != null) {
tempRow = (EmpVORowImpl)getEmpVO().createRow();
tempRow.setAttributeValues(Arrays.asList(tempRow.getAttributeNames()), Arrays.asList(empAttributes));
empRespList.add(tempRow);
}
}
}
} catch (SQLException sqlex) {
throw new Exception("sql exception");
} catch (Exception ex) {
throw new Exception("exception");
} finally {
try {
stmt.close();
} catch (SQLException sqlex) {
throw new Exception("sql exception");
}
}
}
return empRespList;
}
I will use this post as a repository of useful links and blogs which I can expect to grow exponentially over the time, so will try to include links on more generalized or important topics of much interest.
Generic:
ADF
– Understanding View Accessors
Adapters
Weblogic
Adapters
– Multiple SOA Suite Revisions with DB Adapter
Mediator
– Message Sequencing Patterns (white paper)
BPEL
– Transaction Boundaries and Rollbacks
Fault Handling
Business Rules
EDN
Continuous Integration
– Deploy SOA Composites using Hudson
OSB
– WLST Fundamentals in context of OSB
– Working with Oracle Security Token Service
– One-Way Authentication Policies in OSB
– Caching Strategies for OSB 11g
– Out-of-Process Coherence Servers
OWSM
BPM
Case Management
SOA Governance
– SOA Governance Through Enterprise Architecture
Fusion Applications
– Fusion Applications Life Cycle
– Request Flow in Fusion Applications
– Asynchronous Web Services in Fusion Applications
– Oracle Fusion DOO – Integrating External Systems using EIL
Customizations
Transactions when using DB Adapter vs ADF BC Service
Published July 1, 2013 ADF , BPEL Leave a CommentTags: ADF, ADF BC Service, DB Adapter
Most of us are well aware of global transactions (XA) when using DB Adapter and setting XADataSourceName during creation of DB Connection Pool. ADF BC Services are widely used in Oracle Fusion Applications at Data Service layer instead DB Adapters. So it’s worth investigating or finding out the scope of transaction when calling these ADF BC Services.
Sample code for this demonstration can be found here and accepts string input, where valid values are ‘DB’ or ‘ADF’.
Conclusion:
- ADF BC Service call does not get enlisted into main BPEL txn, so system fault, user defined fault or Roll back fault will not revert the changes done by ADF BC Service. So developer should think of compensation in failure scenarios in case of faults.