Wednesday, July 15, 2009

Auto-refreshing ADF11g Gantt Project control

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 11g

Step 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 TaskView


You 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 Interfaces

Step 10. Create a new JSPX page - MyGantt.jspx with Managed Bean

Step 11. You should have the following in your Data Control Palette



Step 12. Drop the SubTaskView1 DataControl as a Read Only Table on the page

Step 13. Drop the SubTaskView1 DataControl as a Read Only Table on the page

In the Table properties set content delivery = immediate

Step 14. Drop the method getLastRequerySubtask() as an ADF button above the table
We do this to get the method binding. Later we will delete the button.

Step 15. Add a Poll control to the page
The 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. Test

Add 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 page

Drag 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 control
We 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 method
We 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 AppModule



Step 24. Run the Page

Set 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 app
We 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 Creation

Use 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
;

1 comment:

Unknown said...

iI wonder if I am getting a problem just because I am using latest version of Jdeveloper. But when I change the property Auto-Refresh to "Always" I start to get the exception: UnsupportedOperationType for registeringDatabaseChangeListener.

Am I required to register any class as a listener for MySQL database or am I missing any step other then what stated?