Jul 27, 2012

Overview of ADF EO and VO classes



Entity Object :

Entity objects in ADF are business components that encapsulate the business model, including data, rules, and persistence behavior, for items that are used in your application.

Entity objects can represent:
  • Elements of the logical structure of the business, such as product lines, departments, sales, and regions.
  • Business documents, such as invoices, change orders, and service requests
  • Physical items, such as warehouses, employees, and equipment .


Entity object definitions comprise three Java classes:
  • The entity collection class - an instance of this class represents the collection of rows currently in memory for a single user.
  • The entity definition class - the singleton instance of this class represents represents the entire data source object.
  • The entity object class - an instance of this class (an entity object instance) represents a single row from the data source. 

    Entity object class : (EOImpl)
    It basically has following methods:
  • Accessors.(setters and getters)
  • Create Method
  • Data Manipulation method.(doDML(int operation, TransactionEvent e))
  • Remove method

Other important EOImpl class methods are:

setAttrInvokeAccessor(),getAttrInvokeAccessor(),createPrimaryKey() and lock().

Accessors:

Attributes in Entity Objects can be of two types:
  1. Persistent attributes : Maps to the data source object columns.
  1. Transient attributes : Doesn't map to any data source object,used mainly for temporary storage and retrieval or simple calculations.

EOImpl class contains getters and setters for all the attributes in EO, so they can be accessed and changed as required.

Eg:getAttribute("Deptno") or setAttribute("Deptno","10").



doDML():
This method is invoked whenever the commit operation is performed.


  1. protected void doDML(int operation, TransactionEvent e) {
  2. if (operation == DML_INSERT) {
  3. /* write PRE-INSERT trigger-like code here */
  4. super.doDML(operation, e); /* Don't call the super and do something
  5. else to write ON-INSERT trigger-like code */
  6. /* write POST-INSERT trigger-like code here */
  7. }
  8. else if (operation == DML_UPDATE) {
  9. /* write PRE-UPDATE trigger-like code here */
  10. super.doDML(operation, e); /* Don't call the super and do something
  11. else to write ON-UPDATE trigger-like code here */
  12. /* write POST-UPDATE trigger-like code here */
  13. }
  14. else if (operation == DML_DELETE) {
  15. /* write PRE-DELETE trigger-like code here */
  16. super.doDML(operation, e); /* Don't call the super and do something
  17. else to write ON-DELETE trigger-like code here */
  18. /* write POST-DELETE trigger-like code here */
  19. }
  20. }

Eg: //in EmpEOimpl's doDML(), after creating a row for Emp table,we are inserting a new row for Department table.


  1. protected void doDML(int operation, TransactionEvent e) {
  2. super.doDML(operation,e);
  3. //after insert operation is done
  4. if(operation==1){
  5. EntityDefImpl dept=DepartmentEOImpl.getDefinitionObject();
  6. DepartmentEOImpl newd= (DepartmentEOImpl)dept.createInstance2(getDBTransaction(), null);
  7. newd.setDepartmentId(c);
  8. newd.setDepartmentName("Finance"+b.toString());
  9. }
  10. }

Note:Don't set the attributes of same EO after calling super.doDML() ,else it throws JBOexception : Post threshold limit reached. Some entities yet to be posted.

EOImpl class methods:

  • protected void prepareForDML(int i, TransactionEvent transactionEvent) {}
    • A pre-notification for preparing rows for posting to DB. If you need to update some dependency columns
       or custom history columns then  add that logic here.
    • Method calls doDML() at the end.
  • protected void doDML(int i, TransactionEvent transactionEvent) {}
    • Does the DML operation. This is where everything gets into the DB. All the INSERT, UPDATE, and DELETE statements for a row executes here.
  • public void beforeCommit(TransactionEvent transactionEvent) {}
    • Reports that a commit operation has initiated
    • Invoked before commit is called. By this time the changes are already present in the DB. So any logic that needs updated data in the DB can be written in this method.
  • public void afterCommit(TransactionEvent transactionEvent) {}
    • Reports that a successful commit operation has occurred.
  • protected void validateEntity() {}
    • Call executes the entity level validations defined on the EO. Gets invoked when use moves from one row to another.
  • public void lock() {}
    • Locks the row.
    • Executes a SELECT FOR UPDATE based on the DML operations, for eg like UPDATE or DELETE
  • protected void create(AttributeList attributeList) {}
    • Apart from declarative defaulting override the method to have custom creation and defaulting logic for attributes.

  • public void remove() {}
    • For delete operation.
  • public void postChanges(TransactionEvent transactionEvent) {}
    • Calls prepareForDML and later doDML to post the changes to DB. Its only after the method's execution data changes are available in the DB before being committed. If application need some PL/SQL or any procedure to be invoked with updated data they can do it here after super or in doDML as needed.
  • public boolean isAttributeUpdateable(int i) {}
    • Returns true if the attribute is update able. Override the method to define own logic, to whether an attribute is updateable or not apart from default WHILE_NEW, NEVER or ALWAYS. The super.isAttributeUpdateable() returns the declarative setting.
    • Eg: Lets say Commission is updateable only when Salary is greater than 5000.This cannot be declaratively achieved. One can write that logic here.
    • <Pseudo Code> 



  1. if(index==comm)
  2. return this.getSal()!=null && this.getSal().doubleValue()>5000
  3. else
  4. return super.isAttributeUpdateable(index) //Default setting for the rest

  • protected void setAttributeInternal(int p1, java.lang.Object p2) { }
    • Triggers attribute level validations. One can see that every attribute setter has setAttributeInternal; It's because of this call the attribute level validations get fired.
  • protected void populateAttribute(int i, Object object) {}
    • Use it to set attribute value by skipping any attribute level validations. The default setAttribute() fires validation after value is set and does not update the target value if any validation fails.
  • protected void populateAttributeAsChanged(int i, Object object) {}
    • Same as above but instructs the framework that the attributes value has changed.


View Object:
ViewObject decribes how the application will view and update data. A View Object may be Entity based or non-Entity based.
View objects are business components that collect data from the datasource, shape that data for use by clients, and allow clients to change that data in the Oracle ADF Business Components cache. For example, a view object can gather all the information needed to:
  • Populate a single table element in a form
  • Create and process an insert or edit form
  • Create an LOV for populating a dropdown list

View Object can be customized on the basis of its Java classes which can be generated:

(i)View Object Class (VOImpl) - an instance of this class is a view object instance; that is, a particular reference to a Oracle ADF view object definition within an application module definition.

(ii)View Row Class(VORowImpl) - an instance of this class represents a single row returned by the view object's mechanism.


VOImpl methods:

  • protected ViewRowImpl createRowFromResultSet(Object object,ResultSet resultSet) {}
    • Executed after the VO SQL query runs.Its here the query result set is iterated and each result is split and pushed to respective entity cache.Override to run any logic to default attribute values based on PL/SQL functions or any other logic after query is executed.One can also create SQL derived attributes for simple SQL procedure or function invocation for an attribute.
  • public void beforeCommit(TransactionEvent transactionEvent) {}
    • Invoked before commit is called. By this time the changes are already present in the DB.So any logic that needs updated data in the DB can be writtern here.
  • public void afterCommit(TransactionEvent transactionEvent) {}
    • Reports that a successful commit operation has occurred.
  • public void postChanges(TransactionEvent transactionEvent) {}
    • Calls prepareForDML and later doDML to post the changes to DB.Its only after the method's execution data changes are available in the DB before being committed. If apps need some PL/SQL or any procedure to be invoked with updated data they can do it here after super or in doDML as needed.
  • public boolean isAttributeUpdateable(int i) {}
    • Same as in EOImpl class.
  • public RowSet createRowSet(String string) {}
    • A rowset is a memory representation for the VO rows. One can create a row set to iterate over rows and access row attributes. ADF has a default rowset and a default rowset iterator. It's always recommended to create one's own rowset and rowset iterator to process rows than manipulating the default rowset. To avoid memory leaks always close rowset and its iterator after processing.
  • public RowSetIterator createRowSetIterator(String string) {}
    • Creates a row set iterator. This iterator is created on the default row set. Always create a custom rowset and create iterator with the custom rowset . To avoid memory leaks always close rowset and its iterator after processing.
  • public Row getCurrentRow() {}
    • The call is primarily routed to default rowset and returns the current selected row. The default rowset is used for UI row currency (meaning current selected row).
  • protected void create() {}
    • Call to create a new row. Any modification for any attribute on call to CREATEINSERT or CREATE can be done here. For eg: Serial No. auto-generation .
  • public Row createRow() {}
    • Call to create a new row in the VO.


ViewRowImpl methods:

  • public void validate() {}
    • Internally invokes validate for corresponding entities.
  • protected void setAttributeInternal(int p1, java.lang.Object p2) { }
    • Same behavior as in Eoimpl class.
  • public boolean isAttributeUpdateable(int i) {}
    • Same behavior as in EO

Convert sysdate to jbo.domain.Date

We can convert current date to oracle.jbo.domain.Date using  java.sql.Date and java.util.Date.

For this we have to take current date in a String variable.

  1.  DateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
  2.  java.util.Date date = new java.util.Date();
  3.  String date1=dateFormat.format(date);

Now change this String date1 to java.util.Date.
As we cannot change date1 to oracle.jbo.domain.Date directly because oracle.jbo.domain.Date format is "YYYY/MM/DD" but this format is not supported in java.util.Date.So, we need to change String date1 to java.util.Date.

  1.  java.util.Date utilDate = dateFormat.parse(date1);


We cannot change java.util.Date to oracle.jbo.domain.Date.
So,we need to change java.util.Date to java.sql.Date and then java.sql.Date to oracle.jbo.domain.Date.


 
  1. java.sql.Date sqlDate = new java.sql.Date(utilDate.getTime());
  2. jboDate = new oracle.jbo.domain.Date(sqlDate);

Configuring ADF security using Database tables:


(Create the required tables as provided at end of this document.Also create the Data Source required)

 
1.Select the Security Realms link > select the default realm "myrealm" .

 

2.Go to Providers tab. Here we can create a new authentication provider.





3.Give a suitable name and select 'SQLAuthenticator' in the Type of authentication provider selection. Click OK.




4.Select your just created provider ('db_users') and change the Control flag to 'Sufficient'. Click Save.





The control flag determines how the SQL Authenticator will behave if the Login Module Succeeds or fails. The possible values and outcomes are as follows:

  • A REQUIRED value specifies this LoginModule must succeed. Even if it fails, authentication proceeds down the list of LoginModules for the configured Authentication providers.
  • A REQUISITE value specifies this LoginModule must succeed. If other Authentication providers are configured and this LoginModule succeeds, authentication proceeds down the list of LoginModules. Otherwise, control is return to the application.
  • A SUFFICIENT value specifies this LoginModule need not succeed. If it does succeed, return control to the application. If it fails and other Authentication providers are configured, authentication proceeds down the LoginModule list.
  • An OPTIONAL value specifies this LoginModule need not succeed. Whether it succeeds or fails, authentication proceeds down the LoginModule list. This setting is the default.

 
5.Go to the 'Provider Specific' tab where we can add the details of the provider.

 

 
Other Settings for detail fields are listed as follows:

data-source-name : HRDS (Whatever DS you want to use for security)

plaintext-passwords-enabled:true (Checkbox)

password-style : PLAINTEXT

sql-get-users-password : SELECT password FROM jhs_users WHERE username = ?

sql-set-user-password : UPDATE jhs_users SET password = ? WHERE username = ?

sql-user-EXISTS : SELECT username FROM jhs_users WHERE username = ?

sql-list-users : SELECT username FROM jhs_users WHERE username LIKE ?

sql-create-user : INSERT INTO jhs_users ( id,username , password , display_name) VALUES (jhs_seq.NEXTVAL, ? , ? , ? )

sql-remove-user: DELETE FROM jhs_users WHERE username = ?

sql-list-groups : SELECT short_name FROM jhs_roles WHERE short_name LIKE ?

sql-group-EXISTS : SELECT short_name FROM jhs_roles WHERE short_name = ?

sql-create-GROUP: INSERT INTO jhs_roles(id, short_name, name) VALUES (jhs_seq.NEXTVAL, ?, ?)

sql-remove-GROUP: DELETE FROM jhs_roles WHERE short_name = ?

sql-is-member : SELECT u.username FROM jhs_user_role_grants g ,jhs_users u WHERE u.id = g.usr_id AND rle_id = ( SELECT id FROM jhs_roles WHERE short_name = ? ) AND usr_id = ( SELECT id FROM jhs_users WHERE username = ? )

sql-list-member-groups : SELECT short_name FROM jhs_user_role_grants g ,jhs_roles r,jhs_users
u WHERE g.usr_id = u.id AND g.rle_id = r.id AND u.username = ?

sql-list-group-members: SELECT username FROM jhs_user_role_grants g ,jhs_roles r,jhs_users u
WHERE g.usr_id = u.id AND g.rle_id = r.id AND r.short_name = ? AND u.username LIKE ?

sql-remove-group-memberships: DELETE FROM jhs_user_role_grants WHERE rle_id = ( SELECT id
FROM jhs_roles WHERE short_name = ? ) OR usr_id = ( SELECT id FROM jhs_users WHERE username = ? )

sql-add-member-to-GROUP : INSERT INTO jhs_user_role_grants (id,rle_id,usr_id) VALUES( jhs_seq.NEXTVAL , ( SELECT id FROM jhs_roles WHERE short_name = ?),(SELECT id FROM jhs_users WHERE username = ?))

sql-remove-member-from-GROUP: DELETE FROM jhs_user_role_grants WHERE rle_id = ( SELECT id
FROM jhs_roles WHERE short_name = ? ) AND usr_id = ( SELECT id FROM jhs_users WHERE username =
? )

sql-remove-group-member: DELETE FROM jhs_user_role_grants WHERE rle_id = ( SELECT id FROM
jhs_roles WHERE short_name = ? )

sql-get-user-description : SELECT display_name FROM jhs_users WHERE username = ?

sql-set-user-description: UPDATE jhs_users SET display_name = ? WHERE username = ?

sql-get-group-description: SELECT name FROM jhs_roles WHERE short_name = ?

sql-set-group-description : UPDATE jhs_roles SET name = ? WHERE short_name = ?
After saving the changes listed above, restart the Weblogic server (mandatory).

After the reboot ,Go the 'User and Group' tab of your default security realm ('myrealm') where we can change or add users and roles. This is similar to adding the roles and users in jazn-data.xml.


Tables required for users and roles:
 CREATE TABLE JHS_ROLES
(
ID NUMBER(*, 0) NOT NULL,
ORG_KEY VARCHAR2(30) DEFAULT 'DEFAULT' NOT NULL,
SHORT_NAME VARCHAR2(10) NOT NULL,
NAME VARCHAR2(40) NOT NULL
);

CREATE TABLE JHS_USER_ROLE_GRANTS
(
ID NUMBER(*, 0) NOT NULL,
USR_ID NUMBER(*, 0) NOT NULL,
RLE_ID NUMBER(*, 0) NOT NULL
);

CREATE TABLE JHS_USERS
(
ID NUMBER(*, 0) NOT NULL,
EMAIL_ADDRESS VARCHAR2(240),
USERNAME VARCHAR2(240) NOT NULL,
ORG_KEY VARCHAR2(30) DEFAULT 'DEFAULT',
PASSWORD VARCHAR2(240),
DISPLAY_NAME VARCHAR2(240),
LOCALE VARCHAR2(10)
);

ALTER TABLE JHS_ROLES
ADD CONSTRAINT JHS_RLE_PK PRIMARY KEY
( ID ) ENABLE;

ALTER TABLE JHS_ROLES
ADD CONSTRAINT JHS_RLE_UK1 UNIQUE
( SHORT_NAME,ORG_KEY ) ENABLE;

ALTER TABLE JHS_USER_ROLE_GRANTS
ADD CONSTRAINT JHS_URG_PK PRIMARY KEY
( ID ) ENABLE;

ALTER TABLE JHS_USER_ROLE_GRANTS
ADD CONSTRAINT JHS_URG_UK1 UNIQUE
( RLE_ID, USR_ID ) ENABLE;

ALTER TABLE JHS_USERS
ADD CONSTRAINT JHS_USR_PK PRIMARY KEY
( ID ) ENABLE;

CREATE SEQUENCE JHS_SEQ INCREMENT BY 1 MAXVALUE 999999999999999999999999999 MINVALUE 1 CACHE 20 ;

-- Create two users SKING and AHUNOLD
INSERT INTO jhs_users (ID, EMAIL_ADDRESS, USERNAME, ORG_KEY, PASSWORD, DISPLAY_NAME)
SELECT jhs_seq.NEXTVAL,'SKING,'SKING','DEFAULT','SKING', 'Steven King'
from dual
where not exists (select '
1' from jhs_users where username='SKING');

insert into jhs_users (ID, EMAIL_ADDRESS, USERNAME, ORG_KEY, PASSWORD, DISPLAY_NAME)
select jhs_seq.nextval,'
AHUNOLD','AHUNOLD','DEFAULT','AHUNOLD', 'Alexander Hunold'
from dual
where not exists (select '
1' from jhs_users where username='AHUNOLD');

-- set up two roles: Administrator and User
insert into jhs_roles(id, SHORT_NAME, name)
select jhs_seq.nextval, '
ADMIN','Administrator'
from dual
where not exists (select '
1' from jhs_roles where short_name='ADMIN');

insert into jhs_roles(id, SHORT_NAME, name)
select jhs_seq.nextval, '
USER','User'
from dual
where not exists (select '
1' from jhs_roles where short_name='USER');

-- Make Steven King Administrator
insert into jhs_user_role_grants (id,rle_id,usr_id)
select jhs_seq.nextval, rle.id, usr.id
from jhs_roles rle, jhs_users usr
where rle.short_name='
ADMIN'
and usr.username='
SKING'
and not exists (select '
1' from jhs_user_role_grants urg2
where urg2.usr_id = usr.id
and urg2.rle_id = rle.id);

-- Make Alexander Hunold User
insert into jhs_user_role_grants (id,rle_id,usr_id)
select jhs_seq.nextval, rle.id, usr.id
from jhs_roles rle, jhs_users usr
where rle.short_name='
USER'
and usr.username='
AHUNOLD'
and not exists (select '
1' from jhs_user_role_grants urg2
where urg2.usr_id = usr.id
and urg2.rle_id = rle.id);

commit;





For Logout from ADF Security we could also invoke the logout by a redirect performed from an action method in a managed bean as follows :

public String logoutAction() {
FacesContext fctx = FacesContext.getCurrentInstance();
ExternalContext ectx = fctx.getExternalContext();
String url = ectx.getRequestContextPath() + "/adfAuthentication? logout=true&end_url=/faces/Homepage.jspx";
try {
ectx.redirect(url);
}
catch (IOException e) {
e.printStackTrace();
}
fctx.responseComplete();
return null;
}
In the above method, HomePage.jspx refers to a public page in the application that the user is redirected to after successful logout.