2012. május 13., vasárnap

Problems with Bindings Defined on Task Flow Call Activities

In an ADF task flow you can define bindings (create a pageDef) on any kind of activity: method call, task flow call, view etc., but there is a problem when trying to access the binding container upon returning from a task flow call activity and trying to copy the called task flow's return value into the bindings, e.g. into the input value of an attribute binding.

For example, consider the following unbounded task flow (adfc-config):


The CallerPage.jspx page shows buttons for the possible control flow cases. In each of them, we call the test-btf bounded task flow, which is defined as follows:


The HelloPage.jspx page has just an input field for #{pageFlowScope.name} and a button to call #{myBean.hello_action}. When clicking the button, the "hello_action" method is called, which calls the "sayHello" method in our POJO and sets its result into #{pageFlowScope.resultGreeting}. E.g. if you enter "John Doe" and click the button, then you will have "Hello, John Doe!" in this parameter. Then the task flow returns to the parent, with a return parameter named "greeting" containing this result. The "myBean" backing bean looks like this:

package pvarga.test.taskflow.returnvalue.view;

import oracle.adf.model.AttributeBinding;
import oracle.adf.model.BindingContext;
import oracle.adf.share.ADFContext;

import oracle.binding.OperationBinding;


/**
 * Testing backing bean.
 * 
 * @author Patrik Varga
 */
public class MyBackingBean {

    public MyBackingBean() {
        super();
    }

    /**
     * Action for "Say hello" the button.
     * 
     * @return "SAY_HELLO"
     */
    public String hello_action() {
        OperationBinding sayHello = BindingContext.getCurrent().getCurrentBindingsEntry().getOperationBinding("sayHello");
        String greeting = (String)sayHello.execute();
        System.out.println("Greeting is: " + greeting);
        ADFContext.getCurrent().getPageFlowScope().put("resultGreeting", greeting);
        return "SAY_HELLO";
    }

    /**
     * Copy #{pageFlowScope.tempReturnedGreeting} intoto #{bindings.storedGreeting.inputValue}.
     */
    public void copyOutputParameter() {
        AttributeBinding storedGreeting = (AttributeBinding)BindingContext.getCurrent().getCurrentBindingsEntry().getControlBinding("storedGreeting");
        String greeting = (String)ADFContext.getCurrent().getPageFlowScope().get("tempReturnedGreeting");
        storedGreeting.setInputValue(greeting);
        System.out.println("Copied value into storedGreeting AttributeBinding: " + greeting);
    }

}

We have the following POJO which we access through ADF data controls and bindings:

package pvarga.test.taskflow.returnvalue.view;

/**
 * Testing POJO, used through POJO DC.
 * 
 * @author Patrik Varga
 */
public class HelloPojo {

    private String storedName;
    private String storedGreeting;
    
    public String sayHello(String name) {
        return "Hello, " + name + "!";
    }

    public void setStoredGreeting(String storedGreeting) {
        this.storedGreeting = storedGreeting;
    }

    public String getStoredGreeting() {
        return storedGreeting;
    }

    public void setStoredName(String storedName) {
        this.storedName = storedName;
    }

    public String getStoredName() {
        return storedName;
    }

}

Now, the three different control flow cases and task flow call activities (test-btf1, test-btf2, test-btf3) are defined as follows:

The CALL_HELLO_BTF_PAGEFLOW case calls the test-btf task flow in the test-btf2 activity. This is just for testing the BTF, it is not using bindings but only pageFlowScope parameters.

The CALL_HELLO_BTF_BINDINGS case calls the test-btf task flow in the test-btf1 activity. This activity has an associated page definition so that we can access the binding container when passing input parameters and receiving output parameters. The input parameter is coming from the bindings (#{bindings.storedName.inputValue}), and the activity is configured to set the "greeting" return value into #{bindings.storedGreeting.inputValue}. Unfortunately, this fails at runtime with the following exception:

javax.el.PropertyNotFoundException: Target Unreachable, identifier 'bindings' resolved to null
        at com.sun.el.parser.AstValue.getTarget(Unknown Source)
        at com.sun.el.parser.AstValue.setValue(Unknown Source)
        at com.sun.el.ValueExpressionImpl.setValue(Unknown Source)
        at oracle.adf.controller.internal.util.ELInterfaceImpl.setExpression(ELInterfaceImpl.java:122)
        at oracle.adfinternal.controller.activity.TaskFlowReturnActivityLogic.storeReturnValues(TaskFlowReturnActivityLogic.java:511)
        at oracle.adfinternal.controller.activity.TaskFlowReturnActivityLogic.execute(TaskFlowReturnActivityLogic.java:200)
        at oracle.adfinternal.controller.engine.ControlFlowEngine.executeActivity(ControlFlowEngine.java:993)
        at oracle.adfinternal.controller.engine.ControlFlowEngine.doRouting(ControlFlowEngine.java:879)
        at oracle.adfinternal.controller.engine.ControlFlowEngine.doRouting(ControlFlowEngine.java:778)
        at oracle.adfinternal.controller.engine.ControlFlowEngine.routeFromActivity(ControlFlowEngine.java:552)
        at oracle.adfinternal.controller.engine.ControlFlowEngine.performControlFlow(ControlFlowEngine.java:148)
        at oracle.adfinternal.controller.application.NavigationHandlerImpl.handleAdfcNavigation(NavigationHandlerImpl.java:109)
        at oracle.adfinternal.controller.application.NavigationHandlerImpl.handleNavigation(NavigationHandlerImpl.java:78)
        at org.apache.myfaces.trinidadinternal.application.NavigationHandlerImpl.handleNavigation(NavigationHandlerImpl.java:43)
        at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:130)
        at org.apache.myfaces.trinidad.component.UIXCommand.broadcast(UIXCommand.java:190)
        at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:475)
        at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:756)
        at oracle.adfinternal.view.faces.lifecycle.LifecycleImpl._invokeApplication(LifecycleImpl.java:788)
        at oracle.adfinternal.view.faces.lifecycle.LifecycleImpl._executePhase(LifecycleImpl.java:306)
        at oracle.adfinternal.view.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:186)
        at javax.faces.webapp.FacesServlet.service(FacesServlet.java:265)
        at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
        at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125)
        at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:300)
        at weblogic.servlet.internal.TailFilter.doFilter(TailFilter.java:26)
        at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
        at oracle.adf.model.servlet.ADFBindingFilter.doFilter(ADFBindingFilter.java:205)
        at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
        at oracle.adfinternal.view.faces.webapp.rich.RegistrationFilter.doFilter(RegistrationFilter.java:106)
        at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl$FilterListChain.doFilter(TrinidadFilterImpl.java:446)
        at oracle.adfinternal.view.faces.activedata.AdsFilter.doFilter(AdsFilter.java:60)
        at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl$FilterListChain.doFilter(TrinidadFilterImpl.java:446)
        at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl._doFilterImpl(TrinidadFilterImpl.java:271)
        at org.apache.myfaces.trinidadinternal.webapp.TrinidadFilterImpl.doFilter(TrinidadFilterImpl.java:177)
        at org.apache.myfaces.trinidad.webapp.TrinidadFilter.doFilter(TrinidadFilter.java:92)
        at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
        at oracle.security.jps.ee.http.JpsAbsFilter$1.run(JpsAbsFilter.java:111)
        at java.security.AccessController.doPrivileged(Native Method)
        at oracle.security.jps.util.JpsSubject.doAsPrivileged(JpsSubject.java:313)
        at oracle.security.jps.ee.util.JpsPlatformUtil.runJaasMode(JpsPlatformUtil.java:413)
        at oracle.security.jps.ee.http.JpsAbsFilter.runJaasMode(JpsAbsFilter.java:94)
        at oracle.security.jps.ee.http.JpsAbsFilter.doFilter(JpsAbsFilter.java:161)
        at oracle.security.jps.ee.http.JpsFilter.doFilter(JpsFilter.java:71)
        at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
        at oracle.dms.servlet.DMSServletFilter.doFilter(DMSServletFilter.java:136)
        at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
        at weblogic.servlet.internal.RequestEventsFilter.doFilter(RequestEventsFilter.java:27)
        at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
        at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.wrapRun(WebAppServletContext.java:3715)
        at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3681)
        at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
        at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:120)
        at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2277)
        at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2183)
        at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1454)
        at weblogic.work.ExecuteThread.execute(ExecuteThread.java:209)
        at weblogic.work.ExecuteThread.run(ExecuteThread.java:178)

I cannot see the reason why this should not work, especially that for task flow input parameters you can use ELs referring the binding container, e.g. to pass the value of an attribute binding as an input parameter, as seen above. It seems that the binding container is released too soon when using task flow call activities.

Moreover, setting the values of attribute bindings works as expected in a method call activity having a binding container, so this can be used as a workaround.

This is what I did in the CALL_HELLO_BTF_BINDINGS_WORKAROUND control flow case of the task flow: instead of immediately copying the task flow call activity's return parameter into the bindings (test-btf3), just copy it to a pageFlowScope parameter (#{pageFlowScope.tempReturnedGreeting}), and in the next method call activity (copyOutputParameter), copy this parameter into the preferred attribute binding (see the MyBackingBean.copyOutputParameter() method).

So, until this problem gets fixed or at least explained, you can use this as a workaround.
Cheers. :)

Nincsenek megjegyzések:

Megjegyzés küldése