Friday, April 4, 2008

Restoring transactions with ADF Taskflow

A great option of ADF taskflow in JDeveloper 11g is restoring savepoints, with this you can continu with your crashed or saved transaction. This can be handy if the user have filled in a lot of forms and he or she don't want to commit yet. The user can decide to store this session, this is called explicit save. The next time the user opens the application. He or she can restore this savepoint and returns to the right page with the right data. An another is option is to enable it for all the bounded taskflow in your application, this is called implicit save. Now the user can continu with its session after a timeout or when the user closes the browser and leaves a transaction open.
You can configure this in the adf-config. the adf-config is located in the .adf\META-INF folder of your application workfolder.

<?xml version="1.0" encoding="windows-1252" ?>
<adf-config xmlns="http://xmlns.oracle.com/adf/config"
xmlns:sec="http://xmlns.oracle.com/adf/security/config">
<sec:adf-config-child xmlns="http://xmlns.oracle.com/adf/security/config">
<CredentialStoreContext credentialStoreClass="oracle.adf.share.security.providers.jps.CSFCredentialStore"
credentialStoreLocation="../../src/META-INF/jps-config.xml"/>
</sec:adf-config-child>
<adfc-controller-config xmlns="http://xmlns.oracle.com/adf/controller/config">
<savepoint-datasource>jdbc/scottDS</savepoint-datasource>
<savepoint-manager>DATABASE</savepoint-manager>
<savepoint-expiration>86400</savepoint-expiration>
<enable-implicit-savepoints>false</enable-implicit-savepoints>
</adfc-controller-config>
</adf-config>

The open transactions can be saved on the the application server or default in a table. In my case I store these savepoints in the database see the element savepoint-manager, now I have to provide the datasource jndi name. If you make a connection in jdeveloper for example scott and then you can fill in jdbc/scottDS. JDeveloper automatically creates datasources for all your project connections.
ADF Taskflow creates now a new table called oradfcsavpt . The records looks like this.

If you use adf security then ADF Taskflow storew the transaction with the logged in username. So you can make a custom restore page where the user can see and restore his own transactions.
If you want to enable savepoint restore for the whole application then you have to use true by the enable-implicit-savepoints element. In my case is this false so I need a method to save the transaction. I will do this with a method call in ADF Taskflow.
In this example I used two taskflows, the first is an unbounded taskflow with a main view, this is a page with a department and employee overview, this taskflow also has a restore page with a simple inputtext where the user can put in the session id. This value is stored in #{pageFlowScope.savepoint} This value is used by the Save Point Restore component.

This is a picture of the main page

The main view call the second bounded taskflow where I can create a new department with a employee.

The first page is a page to create a new department, the second page is to create employees with this new department. On this page I have a commit and a save button.

The save button calls the following method #{controllerContext.currentViewPort.createSavePoint}. This creates a savepoint. The result of this method is the stored session id.

The return value is stored in the #{pageFlowScope.saveId} variable and displayed in the next page.
Now we can close the browser and open the application again. Now go the restore page, put in the session id and restore. You are now redirected to the employee page and continu the transaction.

Here you can download the example project.

22 comments:

  1. Very intresting. I have tried to run your example, but I get this error:

    javax.el.ELException: oracle.adfinternal.controller.savepoint.SavePointException: ADFC-08012: The specified datasource JNDI name, 'jdbc/scottDS', could not be found.

    The application module is working fine, all this time.

    ReplyDelete
  2. Hi,

    'jdbc/scottDS' is the jndi lookup in oc4j11g with Weblogic you should use

    <adfc-controller-config xmlns="http://xmlns.oracle.com/adf/controller/config">
    <savepoint-datasource>java:comp/env/jdbc/scottDS</savepoint-datasource>
    <enable-implicit-savepoints>true</enable-implicit-savepoints>
    </adfc-controller-config>

    ReplyDelete
  3. Thanks Edwin,

    Your example works fine.

    You can change

    jdbc/scottDS
    with

    java:comp/env/jdbc/scottDS


    in your adf-config.xml file from your example zip archive.

    Keep up the good work!

    ReplyDelete
  4. Hello,

    I have tried the same functionality using a train bounded flow.
    It doesn't work. The trainModel is not deserialized correctly at restore.

    ReplyDelete
  5. Hi Do you mean that the train steps jsf component is not restored correctly, that's right.

    In the next patch this will be fixed.

    thanks

    ReplyDelete
  6. Hi.
    I have tried to restore the saved state, but got the error:
    oracle.adfinternal.controller.savepoint.SavePointException: ADFC-08028: Cannot restore the save point. Unable to get the save point with id = '5436a5f2-e12d-4d71-b8d8-410b0dc5519d'.

    ReplyDelete
  7. Hi,

    Strange do you always get this error and do you see the savepoints rows in the database table

    thanks Edwin

    ReplyDelete
  8. How can I make savePoint on page load and restore it after button cancel pressed?

    Best regards, Kriss

    ReplyDelete
  9. Hi

    restoring is easy , use the TF action for this. But for creating you need the have an open transaction.

    So before you open a Bounded TF , you can add a method action to the TF in this managed bean do some create or update actions and then go the view and by leaving the view do a commit or a rollback.

    thanks

    ReplyDelete
  10. Hi, Biemond.
    I build a App just like your sample.I meet a strange problem:
    When I close the browser and open the application again, I click on the button resore, it does not navigate to the restore page.
    I must restart the App, then revisit the main.jsp, this time, click on restore button go to the restore page.
    Do you know the reason?

    ReplyDelete
  11. Hi,

    I dont know, can you do a refresh of your savepoint or something.

    thanks

    ReplyDelete
  12. oracle.adfinternal.controller.savepoint.SavePointException: ADFC-08026: The ADF Controller is unable to deserialize the savepoint.
    I am getting the above exception, can you please help, I even tried maing the class serializable, but it didnt help.

    ReplyDelete
  13. I tried to implement the above example. I created three jspx pages, associated them to a bounded task flow with fragments. Flow goes from page 1 to page 2, then to a method to create an explicit save point, which returns an action, that goes to page 3. Also I used save point restore in the third page, that collects the same save point id stored in the 1st method. When I hit the restore in page 3, it takes me to page 2, but doesn't populate the data which was present in page 2. I have also created backing bean associated with page 2. I am not sure If I have understood the concept of save point restore wrongly, but the intention is in explicit save point, when the user tries to go back to previous page, having some input text fields etc, he should be able to see the data which he had entered earlier. Can you please help. The task-flow-defination.xml is as below.




    view1




    createSavePointBean
    demo.model.CreateSavePointBean
    pageFlow


    backingbean
    demo.model.backingbean
    backingBean


    /train1.jsff



    /train2.jsff



    /train3.jsff


    #{pageFlowScope.createSavePointBean.createSavePoint}

    third



    #{pageFlowScope.createSavePointBean.id}


    view1

    *second
    view2



    view2

    *Save
    createSavePoint



    createSavePoint

    third
    view3



    *

    *back2page
    savePointRestore1

    ReplyDelete
  14. I was able to overcome the above problem by changing the scope of the bean. However, currently, I am able to go back to previous page but unable to restore the values depending on the save point id. In the third page, I give the save the savepoint id I want to restore manually in an input box, it goes back to previous page, however populates the data stored in the latest bean , rather than populating data depending on the save point which I had given. Hence I am unable to use the restore save point feature, can you please help.?

    ReplyDelete
  15. Hi,

    are you using adf bindings as data input or a managed bean.

    I think it only works on adf binding or in managed bean in view scope.
    it wont restore the values in a session managed bean , that why you see the current values.

    make a java class and generate an ADF datacontrol and use this ADF in your page.

    thanks

    ReplyDelete
  16. Hi,
    I am trying to restore the value of the save point on load of the page.
    I tried phase listener for this but i am not able to restore the values.
    Please guide me on this. What configuration i have to do to restore the savepoint on load of page.

    ReplyDelete
  17. Hi,

    you need to do in the ADF Task Flow before you enter the view.

    thanks

    ReplyDelete
  18. Hi,

    I get the following error when trying to restore.

    oracle.adfinternal.controller.savepoint.SavePointException: ADFC-08028: The ADF Controller is unable to restore the savepoint with ID = '35183874-c652-4727-96cc-db498f5e2194'.
    at oracle.adfinternal.controller.savepoint.SavePointUtil.createAndLogSavePointException(SavePointUtil.java:54)
    ......

    ReplyDelete
    Replies
    1. Hi,

      did you use it on a Bounded Task Flow and I don't think it is working on fragment region.

      thanks

      Delete
  19. Hi Edwin,

    Thanks for the post. I created similar application but facing two issues

    1) After saving the transaction when i am clicking Return on savePointNumber page, the entry in ORADFCSAVPT table is getting deleted. Due to this i am getting below exception oracle.adfinternal.controller.savepoint.SavePointException: ADFC-08028: The ADF Controller is unable to restore the savepoint with ID = '42e29b2c-ea5b-4652-865d-3bb1c1eb1659'

    2) If i stay on the saveNumberPoint page (to keep the DB values) and open the main page in another tab, the restore works but i don't get the values saved in the employees form. Only departmentId is populated.

    Can you please suggest what could be the issue.

    Thanks

    ReplyDelete
    Replies
    1. Hi,

      are you using page fragments, only works with full page jspx pages.
      and you should add autosubmit to the fields else the server side won't get your values.

      thanks

      Delete
  20. Hi Edwin,
    Good example I tried your sample example but I am getting the error
    javax.el.ELException: oracle.adfinternal.controller.savepoint.SavePointException: ADFC-08012: The specified data source JNDI name, 'jdbc/scottDS', could not be found.
    Caused by: javax.naming.NameNotFoundException: Unable to resolve 'jdbc.scottDS'. Resolved 'jdbc'; remaining name 'scottDS'

    I tried with the explanation in the comments of this post but still I am getting the same error Could u please tell me how to fix this issue.

    Regards,
    Rajendra.

    ReplyDelete