The scenario is as follows - I have 2 simple tables - TASK and SUBTASK
(SQL ddl script is at the end of this post)
I want to display the data in a Gantt project control as follows -
I also want this control to auto-refresh when the underlying data changes.
Step 1. Create an ADF Fusion APP in JDev 11gStep 2. In the Model Project - add ADF Business Components --> Business Components for Tables. Select the tables - TASK and SUBTASK.
Step 3. Add an extra ViewObject based on SUBTASK specifically for the Gantt chart.
Step 4. Create a new ViewLink between the new ViewObject and the TaskViewYou should end up with something like this -
Step 5. Set the property AUTO-REFRESH = true for all the View Objects.
Step 6. Create the Java ViewObjectImpl classes for TaskView and SubtaskView.
Step 7. Add the following code after the constructor in TaskViewImpl.long lastRequeryTask = 0;
public long getLastRequeryTask() {
return lastRequeryTask;
}
@Override
protected void bindParametersForCollection(QueryCollection queryCollection,
Object[] object,
PreparedStatement preparedStatement) throws SQLException {
super.bindParametersForCollection(queryCollection, object, preparedStatement);
if (queryCollection != null) {
lastRequeryTask = System.currentTimeMillis();
}
System.out.println("Re-execute "+(queryCollection == null?"count ":"")+"query for VO "+getName());
}
@Override
protected void processDatabaseChangeNotification(QueryCollection queryCollection) {
System.out.println("*** TaskViewImpl DatabaseChangeNotification");
lastRequeryTask = System.currentTimeMillis();
super.processDatabaseChangeNotification(queryCollection);
}
Step 8. Add the following code after the constructor in SubtaskViewImpl.
long lastRequerySubTask = 0;
public long getLastRequerySubTask() {
System.out.println("SubtaskViewImpl: getLastRequerySubTask() returns " + lastRequerySubTask);
return lastRequerySubTask;
}
@Override
protected void bindParametersForCollection(QueryCollection queryCollection,
Object[] object,
PreparedStatement preparedStatement) throws SQLException {
super.bindParametersForCollection(queryCollection, object, preparedStatement);
if (queryCollection != null) {
lastRequerySubTask = System.currentTimeMillis();
System.out.println("SubtaskViewImpl: bindParametersForCollection() set lastRequerySubTask to " + lastRequerySubTask);
}
System.out.println("Re-execute "+(queryCollection == null?"count ":"")+"query for VO "+getName());
}
@Override
protected void processDatabaseChangeNotification(QueryCollection queryCollection) {
System.out.println("*** TaskViewImpl DatabaseChangeNotification");
lastRequerySubTask = System.currentTimeMillis();
System.out.println("SubtaskViewImpl: processDatabaseChangeNotification() set lastRequerySubTask to " + lastRequerySubTask);
super.processDatabaseChangeNotification(queryCollection);
}
Step 9. Expose both methods via the Client InterfacesStep 10. Create a new JSPX page - MyGantt.jspx with Managed BeanStep 11. You should have the following in your Data Control PaletteStep 12. Drop the SubTaskView1 DataControl as a Read Only Table on the pageStep 13. Drop the SubTaskView1 DataControl as a Read Only Table on the pageIn the Table properties set content delivery = immediate
Step 14. Drop the method getLastRequerySubtask() as an ADF button above the tableWe do this to get the method binding. Later we will delete the button.
Step 15. Add a Poll control to the pageThe poll control will contact the middle tier ViewObject every 2 seconds to see if it has received database notifications.
Step 16. Add the following Poll logic to the managed bean public void onPollTimerExpired(PollEvent pollEvent) {
Long lastRequerySubTask = (Long)AdfFacesContext.getCurrentInstance().getViewScope().get("lastRequerySubTask");
Long lastRequerySubTaskFromVO = (Long)BindingContext.getCurrent().getCurrentBindingsEntry().getOperationBinding("getLastRequerySubTask").execute();
System.out.println("+++++ Sub Task Timer expired: lastRequerySubTask = "+lastRequerySubTask+", lastRequerySubTaskFromVO = "+lastRequerySubTaskFromVO);
if (lastRequerySubTask == null || (!lastRequerySubTask.equals(lastRequerySubTaskFromVO))) {
AdfFacesContext.getCurrentInstance().getViewScope().put("lastRequerySubTask",lastRequerySubTaskFromVO);
AdfFacesContext.getCurrentInstance().addPartialTarget(getT1());
System.out.println("++++ Data requeried in VO. PPR'ing the table");
}
}
Set the poll control properties as follows -
Step 17. TestAdd a couple of rows to the DB tables -
delete from subtask;
delete from task;
commit;
insert into task values ('Task 1', 'Task Nr 1', sysdate, sysdate + 10);
insert into subtask values ('SUBTask 1', 'SUBTask Nr 1', sysdate, sysdate + 5, 'Task 1');
insert into subtask values ('SUBTask 2', 'SUBTask Nr 2', sysdate+5, sysdate + 10, 'Task 1');
commit;
Run the page
You should see the following -
Now add a new row to the table via SQLPLUS -
insert into subtask values ('SUBTask 3', 'SUBTask Nr 3', sysdate+5, sysdate + 10, 'Task 1');
commit;
The table should auto-refresh.
So Auto-Refresh is working fine for the table, so let's apply the same logic to the Gantt Project control.
Step 18. Add a Gantt Project Control to the pageDrag and Drop TaskView1 as a Gantt Project Control above the table control (use the Structure panel for this)
Set Task Id = TaskId
Start Time = Start Date
End Time = End Date
Click on the Subtasks tab -
For Subtasks Accessor select the View SubTask4Gantt
Set Sub Task Id = TaskId
Start Time = Start Date
End Time = End Date
Step 19. Drop the method getLastRequeryTask() as an ADF button above the Gantt controlWe do this to get the method binding. Later we will delete the button.
Step 20. Add another poll control which will call this method via backing bean methodWe do this to get the method binding. Later we will delete the button.
Your structure panel should look something like this -
Step 21. Add the following method to the backing bean for polling on Task changes public void onPollTimerExpired1(PollEvent pollEvent) {
Long lastRequeryTask = (Long)AdfFacesContext.getCurrentInstance().getViewScope().get("lastRequeryTask");
Long lastRequeryTaskFromVO = (Long)BindingContext.getCurrent().getCurrentBindingsEntry().getOperationBinding("getLastRequeryTask").execute();
System.out.println("*** Task Timer expired: lastRequeryTask = "+lastRequeryTask+", lastRequeryTaskFromVO = "+lastRequeryTaskFromVO);
if (lastRequeryTask == null || (!lastRequeryTask.equals(lastRequeryTaskFromVO))) {
AdfFacesContext.getCurrentInstance().getViewScope().put("lastRequeryTask",lastRequeryTaskFromVO);
AdfFacesContext.getCurrentInstance().addPartialTarget(getPg1());
System.out.println("**** Data requeried in VO. PPR'ing the gantt");
}
Also add this line of code to the method onPollTimerExpired to PPR on the Gannt as well
AdfFacesContext.getCurrentInstance().addPartialTarget(getPg1());
Step 22. Set the 2nd poll control properties as follows -Step 23. set Databinding.cpx to use Shared AppModuleStep 24. Run the PageSet up test data -
delete from subtask;
delete from task;
commit;
insert into task values ('Task 1', 'Task Nr 1', sysdate, sysdate + 10);
insert into subtask values ('SUBTask 1', 'SUBTask Nr 1', sysdate, sysdate + 5, 'Task 1');
insert into subtask values ('SUBTask 2', 'SUBTask Nr 2', sysdate+5, sysdate + 10, 'Task 1');
commit;
View the control and open to see the subtasks -
Add a new Subtask via SQLPLUS
insert into subtask values ('SUBTask 3', 'SUBTask Nr 3', sysdate+5, sysdate + 10, 'Task 1');
commit;
The Gantt chart will auto-refresh.
Add a new Task via SQLPLUS
insert into task values ('Task 2', 'Task Nr 2', sysdate, sysdate + 10);
commit;
The Gantt chart will auto-refresh.
25. Cleanup the appWe only need 1 poll control and 1 managed bean onPollTimerExpired method.
So include the code from onPollTimerExpired1() in onPollTimerExpired().
Delete the second poll control.
I only use the table control here because I have some issues with auto-refresh on the SubTask4Gantt view. All we really need is the iterator SubtaskView1Iterator and
the following code -
In SubtaskViewImpl -
public void executeTheQuery(){
ViewObject vo = this.getViewObject();
vo.executeQuery();
}
expose this method thru the client interface.
drop this method as a Button on the page and double-click to generate
the code in the backing bean -
public String executeTheQuery_action() {
BindingContainer bindings = getBindings();
OperationBinding operationBinding = bindings.getOperationBinding("executeTheQuery");
Object result = operationBinding.execute();
if (!operationBinding.getErrors().isEmpty()) {
return null;
}
return null;
}
We can call this method as the page is being instantiated.
So now we can delete the table control.
Why do I need the 2nd SubTask Iterator?Essentially we shouldn't need this, I just had issues getting the database notification to work with the subtaskView in the Gantt. This my workaround of creating the 2nd iterator "independent" of the Gantt.
In a further post I will hopefully be able to report on what was causing this issue
and how I fixed it!
SQL for Table CreationUse Oracle DB 11g
activate database notification for schema owner
CREATE TABLE TASK
(
TASK_ID VARCHAR2(10 BYTE) NOT NULL,
TASK_NAME VARCHAR2(20 BYTE) NOT NULL,
START_DATE DATE NOT NULL,
END_DATE DATE
, CONSTRAINT TASK_PK PRIMARY KEY
(
TASK_ID
)
ENABLE
)
;
CREATE TABLE SUBTASK
(
SUBTASK_ID VARCHAR2(10 BYTE) NOT NULL,
SUBTASK_NAME VARCHAR2(20 BYTE) NOT NULL,
START_DATE DATE NOT NULL,
END_DATE DATE,
TASK_ID VARCHAR2(10 BYTE) NOT NULL
, CONSTRAINT SUBTASK_PK PRIMARY KEY
(
SUBTASK_ID
)
ENABLE
)
;
ALTER TABLE SUBTASK
ADD CONSTRAINT SUBTASK_TASK_FK1 FOREIGN KEY
(
TASK_ID
)
REFERENCES TASK
(
TASK_ID
)
ENABLE
;