Forum Stats

  • 3,782,455 Users
  • 2,254,650 Discussions
  • 7,880,081 Comments

Discussions

ADFC-00006 after commiting transaction on dynamic region

RodCS
RodCS Member Posts: 22 Red Ribbon
edited May 1, 2016 2:45PM in JDeveloper and ADF

I'll explain my use case, what I've tried to do and where I'm stuck now.

I've a main bounded task flow with the Transaction attribute set to Always begin new transaction and a view activity inside this task flow.

Now inside this view I've a dynamic region and a few dynamic region links, the task flows that I use as region have the Transaction attribute set to Always use existing transaction.

On the method that sets the current region I have the following code that creates a new Transaction just in case a region close it by commiting or rollingback:

    public String cadastroManutencaoDeployTaskFlow() {
        createTransaction();
        taskFlowId =
                "/WEB-INF/cadastroManutencaoDeployTaskFlow.xml#cadastroManutencaoDeployTaskFlow";
        return null;
    }

    public void createTransaction() {
        BindingContext context = BindingContext.getCurrent();


        String dcFrameName = context.getCurrentDataControlFrame();
        DataControlFrame dcFrame = context.findDataControlFrame(dcFrameName);
        context.dataControlsIterator();
        if (dcFrame.getOpenTransactionName() == null) {
            dcFrame.beginTransaction(new TransactionProperties());
        }
    }


The problem I'm facing is that when I do a commit or rollback in the region and change the current region the ADFC-00006 error is getting thrown.

Shouldn't this piece of code guarantee that a new transaction is always created before entering the new region??

getOpenTransactionName() is never null (doesn't this means that the transaction is active? If so why am I getting this error?) even after the commit or rollback, so the beginTransaction is never called in this case, but I've removed the condition and called the beginTransaction directly.

Any help is appreciated, thanks.

Tagged:
amey g

Best Answer

  • RodCS
    RodCS Member Posts: 22 Red Ribbon
    edited May 1, 2016 2:45PM Accepted Answer

    Well, finally got this working as intended. This hapenned after changing all task flows to <No controller transaction>.

    I changed the rollback method on the finalizer to call rollback on the application module instead of the data control frame and it worked, so I started to think that since the data control scope is shared with the parent task flow, maybe the parent task flow wasn't being rolled back and the reason for it was probably because the parent task flow wasn't creating the transaction(ofc it wasn't since there were no calls to the data control on the parent, there were no bindings).

    So I just changed the parent task flow to Always begin new transaction, and the finalizer method to call the rollback on the data control frame again and it's working, I delete the row on the first region, rollback, navigate to the second and when I'm back to the first region the row is there.

    So the final solution is this:

    1. Parent taskflow Always begin transaction

    2. All child taskflows <No controller transaction>

    3.

        public void finalizeTaskFlow() {

            BindingContext bc = BindingContext.getCurrent();

            String dcfName = bc.getCurrentDataControlFrame();

            DataControlFrame dcf = bc.findDataControlFrame(dcfName);

            if (dcf.isTransactionDirty()) {

                 dcf.rollback();

            }

        }

    Do you think this solution is correct? Thanks.

    amey g

Answers

  • amey g
    amey g Member Posts: 2,631 Gold Trophy
    edited Apr 29, 2016 9:58PM

    Hello,

    A bounded task flow that uses the "Always Use Existing Transaction" option is designed to share the transaction of the previous task flow in your application, it will not start a new transaction. When opened it enforces that it is joining an existing transaction of the previous task flow otherwise it throws "ADFC-00006: Existing  transaction is required when calling task flow <task flow name>" at runtime.

    How this is enforced is through the fact this option is only available with a shared data control scope implying it always shares the previous task flow's data control frame.

    For a bounded task flow using the "Always Use Existing Transaction", on closing it cannot make use of a commit or rollback task flow return activity, at design time such a task flow return  activity will be flagged in error. Only a task flow that starts a new transaction can call these. In this case the "Always Use Existing Transaction" option will depend on its caller to finalize the transaction. Instead the "Always Use Existing Transaction" task flow simply calls a task flow return activity with its End Transaction property set to "<Default> None". If at runtime an "Always Use Existing Transaction" bounded task flow does use specify a commit or rollback.

    Thanks,

    Amey

    RodCS
  • RodCS
    RodCS Member Posts: 22 Red Ribbon
    edited Apr 29, 2016 10:59PM

    Sorry, forgot to say how I'm doing rollback, I'm not using the task flow return activity, I'm using this piece of code on the task flow finalizer method:

        public void finalizeTaskFlow() {

            BindingContext bc = BindingContext.getCurrent();

            String dcfName = bc.getCurrentDataControlFrame();

            DataControlFrame dcf = bc.findDataControlFrame(dcfName);

            System.out.println("ROLLBACK TRANSACTION NAME: "+dcf.getOpenTransactionName());

            if (dcf.isTransactionDirty()) {

                dcf.rollback();

            }

        }

    But I guess the result will be the same, the taskflow that shouldn't end the transaction will be doing it. I'll review my task flows.

    Even though  it is not correct, changing the code to this, worked

        public void finalizeTaskFlow() {

            BindingContext bc = BindingContext.getCurrent();

            String dcfName = bc.getCurrentDataControlFrame();

            DataControlFrame dcf = bc.findDataControlFrame(dcfName);

            System.out.println("ROLLBACK TRANSACTION NAME: "+dcf.getOpenTransactionName());

            if (dcf.isTransactionDirty()) {

                dcf.rollback();

                BindingContext context = BindingContext.getCurrent();

                String dcFrameName = context.getCurrentDataControlFrame();

                DataControlFrame dcFrame = context.findDataControlFrame(dcFrameName);

                context.dataControlsIterator();

                if (dcFrame.getOpenTransactionName() == null) {

                    dcFrame.beginTransaction(new TransactionProperties());

                }

            }

        }

    What is the explanation behind this? Creating a new transaction from a region works, but creating it from the parent won't work.

  • amey g
    amey g Member Posts: 2,631 Gold Trophy
    edited Apr 30, 2016 1:06AM

    Hello,


    If the "Always Begin New Transaction" option is coupled with a shared data control scope, this means the previous task flow's data controls are shared with the current task flow. As a result the pre-existing data controls may already have an open transaction (i.e. isTransactionDirty() == true). If this is true where an existing transaction is detected at runtime the framework will throw "ADFC-00020 + Task flow '<name>' requires a new transaction, but a transaction is already open on the frame". If a bounded task flow does start with a new transaction, when it wishes to complete it must call a task flow return activity commit or rollback. These call the underlying commit() or rollback() operations on the associated data control frame, essentially committing or rolling back all data controls attached to the frame.

    programmatically you can call the following code which has the same result:
    BindingContext bc = BindingContext.getCurrent();
    String dcfName = bindingContext.getCurrentDataControlFrame();
    DataControlFrame dcf = bc.findDataControlFrame(dfcName);
    dcf.commit(); // or dcf.rollback();

    Please check below ref:

    http://one-size-doesnt-fit-all.blogspot.in/2011/05/jdev-11g-task-flows-adf-bc-always-use.html

    Thanks,

    Amey

    RodCS
  • RodCS
    RodCS Member Posts: 22 Red Ribbon
    edited May 1, 2016 1:40PM

    When you say the previous task flow's data, you're saying the previous task flow invoked on the dynamic region, not the parent task flow, right?

    I decided to use <No controller transaction> but with Shared data control enabled.

    But now I'm seeing an odd behavior, I'm still using the same code to rollback the changes when I exit the region, meaning that when the task flow is finalized if the transaction is dirty it will be rolled back.

    What is happening is the following:

    I enter the parent task flow, click on a commandLink that will render a taskflow as a dynamic region, on this taskflow I have a table with a couple of itens, I proceed to delete one item on this table but do not commit and simply click on  another commandLink that will render another taskflow, when I do this the taskflow finalizer is called and it detects that the transaction is dirty and rollback the changes made and then the new region is rendered, but when I come back to the previous region the item is not there I need to clear the controller state on the URL.

    If I disable the Share data control it works, when I navigate away and come back to the previous task flow the item is there.

    If I create the same iterator that is used to display the table on the parent too and refresh this iterator before entering the task flow it also works(WIth Share data control enabled).

  • RodCS
    RodCS Member Posts: 22 Red Ribbon
    edited May 1, 2016 1:55PM

    I've overriden the prepareSession method on the AppModuleImpl and placed a println just to see when the data control is recycled, and it is always being called then I enter the dynamic region with Share data control disabled, I guess this is why this is working, it is getting everything from the DB again, right?

    With Share data control enabled this method is not called when I enter the region, so I guess that this is why it only works like this when I refresh the iterator on the parent task flow, it is still using the data control frame of the parent task flow. But this refresh on the parent should happen automatically since I have ClearCacheOnRollback="true" on the AM, correct?

    Does anything that I said makes sense?

  • RodCS
    RodCS Member Posts: 22 Red Ribbon
    edited May 1, 2016 2:45PM Accepted Answer

    Well, finally got this working as intended. This hapenned after changing all task flows to <No controller transaction>.

    I changed the rollback method on the finalizer to call rollback on the application module instead of the data control frame and it worked, so I started to think that since the data control scope is shared with the parent task flow, maybe the parent task flow wasn't being rolled back and the reason for it was probably because the parent task flow wasn't creating the transaction(ofc it wasn't since there were no calls to the data control on the parent, there were no bindings).

    So I just changed the parent task flow to Always begin new transaction, and the finalizer method to call the rollback on the data control frame again and it's working, I delete the row on the first region, rollback, navigate to the second and when I'm back to the first region the row is there.

    So the final solution is this:

    1. Parent taskflow Always begin transaction

    2. All child taskflows <No controller transaction>

    3.

        public void finalizeTaskFlow() {

            BindingContext bc = BindingContext.getCurrent();

            String dcfName = bc.getCurrentDataControlFrame();

            DataControlFrame dcf = bc.findDataControlFrame(dcfName);

            if (dcf.isTransactionDirty()) {

                 dcf.rollback();

            }

        }

    Do you think this solution is correct? Thanks.

    amey g
This discussion has been closed.