Friday, November 5, 2010

Bookmarking Taskflows in ADF / Accessing ADF Taskflows by URLs


The problem

I need to bookmark or deep link a page in an ADF application, either to save a link to the page or share the page by email or other means. Unlike a normal web-app, JSF does not play well with bookmarks, but ADF comes bundled with (somewhat limited)support for doing this. This sort of a requirement is common when we need to include an application link in a generated email report to enable to user to see additional interactive information or to secure the information itself by sending not the report but just a link to it, so that he user will authenticate before accessing the information.


The solution

ADF has bookmark support for pages in the unbounded task flow, so we take a look at how the bookmark attribute works and how to use it. The built in support has limitations in that only pages in the unbounded taskflow can be bookmarked, and at times we would want pages within a bounded taskflow to be bookmark-able, so we take a look at a method for bookmarking/deep-linking a page within a bounded taskflow.


Bookmarking in the unbounded taskflow

The following example of the out-of-the-box support for bookmarking is based on the standard Oracle documentation here.

You start out with a standard app, in this case a page that lists  the departments in the HR schema and a second page that displays the details of  the staff in a given department. We need to make this second page bookmark-able, i.e. we need the ability to directly give a URL with parameters and pull the staff information of the department that we use as the parameter in the URL. The taskflow is simple and is shown below.
click to enlarge

You can download the full example for the complete code, but the essentials of the example are these :
  • The first page lists the depatments table, and the second page gets the staff details for a specific department 
  • The second page is using a sql query (ViewObject) that has the departmentName as a bind parameter.
  • You have the first page(welcome) with the department list(from DEPARTMENTS table in HR schema) displayed on a table, and each row has an <af:commandButton> that has its action attribute set to a navigation rule that directs the flow to the staff details page.
  • The  <af:commandButton> also takes care of passing the parameter to the details page.(code below)
click to enlarge


 Now, for the purpose of the example, the command button has an action listener that is bound to a custom method on the AM that takes the department name as a method parameter and executes the Staff Details VO(this dept name being a bind param for the VO). An <af:setActionListener> sets the dept name corresponding to the button in to a backing bean property. Then in the pageDef for the Department page, an operation binding for the AM method(via DataControl) created and is configured to pickup the backing bean property as a parameter to the AM method. So when the button is clicked: 
  1. The <af:setActionListener>  copies the respective department name to a backing bean property 
  2. Then the actionListener for the button fires executing the method binding, 
  3. The method binding takes the department name from the backing bean property(set in step 1), and uses it as a parameter to the AM method. 
  4. This AM method in turn executes the StaffdetailsVO binding the given department name to it. 
  5. Now the staff details VO contains the staff information for the given department. 
click to enlarge

AM Method - click to enlarge


Then, since the action attribute of the command button was set to a navigation rule to the staff page, the Staff Details page loads displaying the staff VO that has been executed already.
click to enlarge



This is the basic working of the example app. Now for bookmarking, we need the ability to extract the URL parameter from the URL and execute the StaffDetailsVO when loading the Staff Details page. ADF provides exactly this.

In the unbounded taskflow, open the property inspector for the view activity representing the staff detail page(Click on the StaffDetail ViewActivity) and you can see there is a properties section for bookmarking. 
click to enlarge

Here you will see the URL parameter name and value. They basically mean that the framework will take whatever value is in the URL for the given name, and it will populate that value to the destination. The optional method property lets you invoke a custom method after the values have been processed and before the page is loaded to do custom processing, like execute a VO with the param we get  from the URL !      

In this case, name is set to “dept”  and value is set to #{pageFlowScope.deptDetails.departmentName}, which is the departmentName property in the backing bean. So essesntially what the framework does is look at the URL for the parameter named “dept”, take the value for tht parameter and populate the value to the backing bean property . Now we also have a custom method defined as #{pageFlowScope.deptDetails.loadDepartmentDetails} which is a method defined in the backing bean as:

click to enlarge

Since the above method is invoked after processing the parameters, the departmentName property of the backing bean has been set to the value specified by the URL param “dept”. So the call to getDepartment will return the department name that was passed though the URL. The rest is like clockwork, we get the same method binding and execute it. Its worthwhile to note here that the method binding here is created on the Staffdetails page’s pageDef and defaults to the backing bean property departmentName for the department name parameter it needs to execute the Staffdetails VO. So setting the operationBinding parameter manually in this method is not required and is shown for demonstration.


Bookmarking in bounded task-flows
Bounded task-flows are a little bit more tricky and before we dive in to the implementation, consider this: When we say URL accessible, we are essentially saying a publicly available resource reachable by a URL, whereas a bounded taskflow is like a component, which needs a container. So we need a way to make the hidden innards of a bounded taskflow accessible in the form of a URL, with optional parameters. One way to do this to setup/construct the state for a page in the bounded taskflow based on the parameters in the URL that we’ll use. Since pages in bounded taskflows cannot be reached directly(actually they can, but the URL is not human readable and is very snarly) the approach here is to create a "container" page in the unbounded taskflow, and embed our bounded taskflow inside the container as a region. Then we will make that "container" page bookmark-able as described above. This way the clients will always see the "container" page's URL  which would be nice and simple too. The bookmark-able page takes the URL parameters and passes them to to the taskflow as taskflow params and the task flow will be constructed so that it can recreate state based on the input parameters. Bounded taskflows do have a property called URL invoke. This enables the taskflow to be invoked with a URL, and is a good option when you want to expose the whole taskflow to be URL accessible, not just a page within the taskflow. We will not look at this option as we are trying to gain access to a specific page/part of a taskflow without going though the taskflow’s normal flow.

About the example
As the example, we’re going to do the exact same app as before, but time as a bounded taskflow with parameters. In the example, we will see the whole flow implemented, from search and details, and this is not necessary in most cases where you want to make a single page of your application bookmark-able/linkable. This is more of a pattern that I have found that will save time, and keeps code consistent. Develop the whole taskflow as bounded and provide the extra “hooks” to parts of your bounded taskflow from the unbounded one. This way, you can reap all the benefits of a bounded taskflow (like reusability, security), and the taskflow consumer can expose parts of the taskflow as URL accessible.


First we design the bounded taskflow. Since this taskflow takes parameters, the taskflow parameters and properties can be set in the property inspector by clicking on the blank area in the taskflow diagram.
click to enlarge


The taskflow starts with a router which decides which page to see. although this is not required, it demonstrates an approach for structuring task flows that are built for URL accessibility(see the “about the example” box note). The important part is the taskflow parameters, as you can see from the property inspector image above. We’ll see how these parameters are passed to the task flow later on. The router looks at one parameter(viewMode) to decide which page page it wants to show. In the example, it is always the StaffDetails page. Once it determines that the details page is to be loaded, it then invokes a MethodCallActivity, passing the second input parameter, which is the deptName, to the method.
This MethodCallActivity has a page definition/ binding container so that it can invoke the AM method to execute the Deatils VO by passing in the dept Name. To open this pageDef file, right click the MethodCallActivity and choose "Go to Page Definition". Once the VO has been executed, the method call outcome loads the StaffDetails page which simple displays the executed VO as before. To make the page URL accessible, we put a “hook” page in the unbounded taskflow that is Bookmarkable and captures the URL parameters. 
click to enlarge


This page contains the bounded taskflow as a region and passes the URL parameters to the taskflow though the page’s pageDef/binding container.
click to enlarge
Well that's it. You can download the sample workspace with both approaches here: http://myadfnotebook.googlecode.com/files/Bookmarking_with_ADF.zip
[Built on Jdev 11.1.1.3.0, HR schema]
To run the example
You can run

  •  welcome.jspx file to see bookmarking in unbounded taskflow
Once the page is run, for URL accessibility you can use URLs of the form :
http://<host>:<port>/Bookmarking_with_ADF-ViewController-context-root/faces/staffDetails?dept=Marketing

You would also notice that when using "Staff Details" button on the welcome page to navigate to the details page, the URL in the browser contains the parameter. Thats right. The name and value pair that was configured works both ways. When navigating to a bookmark-able page, the URL is constructed based on this name/value pair by evaluating the value and putting it on the URL as a named parameter. Just the way the same value is extracted from the URL.
  • BoundedFlowContainer.jspx for the bounded task flow technique 
The example wont display anything by default, unless you put a deptName named parameter on the URL.
Once the page is run, for the bounded flow you can use the URL of the form :
http://<host>:<port>Bookmarking_with_ADF-ViewController-context-root/faces/BoundedFlowContainer?deptName=IT (or any other dept name)


20 comments:

  1. Hi,

    Thanks for this very helpful post. We have similar requirement. Two taskflows (one which displays search results and other taskflow displays the details of the selected result).

    The results taskflow is a bounded taskflow which has the URL Invoke property set to "url-invoke-allowed".

    I am able to invoke to taskflow independently form the URL :

    http://127.0.0.1:7101/Bookmarking_with_ADF-ViewController-context-root/faces/adf.task-flow?adf.tfId=details-bounded-task-flow-definition&adf.tfDoc=/WEB-INF/details-bounded-task-flow-definition.xml&documentName=TestDoc

    documentName being the parameter. This is the link which would be bookmarked.

    I am trying to find a way to call this form another bounded task flow. I couldn't find a way to programmatically compute this URL. Please let me know if there is a way to invoke this taskflow without hardcoding the URL.

    Thanks,
    Manjunath

    ReplyDelete
  2. Manjunath,
    This is surely possible, and my suggestion is not to use the default URL that ADF cretes for you (like the one in your comment), as you've already found out, its hard to compute this on our own.

    What you need is a "hook" that can be easily accessed and you can pass only the parameters you need, documentName in your case, without worrying about anything else.

    Take a look at the approach described in the second part of the article,"Bookmarking in bounded task-flows", and you should see a solution. The thing is to keep a "landing page" outside, in the unbounded taskflow, so that you can compute the URL to this page easily, and then invoke your taskflow from that page. You dont have to hard code any of your parameters. The "landing page"/"hook" in the unbounded task flow will be marked bookmarkable and takes the parameters you need through the URL. So the URL would look like

    http://localhost:port/context_root/LangingPage?documentName=TestDoc

    Then it calls your taskflow and passes those parameters it got from the URL to your taskflow. So you'll never have to compute that complex URL. The code sample for this article should make it clear for you on how to capture parameters in your landing page and pass them on to your bounded taskflow. Let me know if you need more help.

    ReplyDelete
  3. In particular, if you have an ADF taskflow and you are in the middle of a task flow, can you have an external application provide a link back to the page where you came from?

    ReplyDelete
  4. Vidya,
    Not sure what you are trying to do, but consider the task flow as a single unit, not a set of pages(its not, unless your taskflow is based on pages and not page fragments).
    So if you design your taskflow to handle URL parameters like in the article above, you could use that to re-create state by passing in a parameter.
    For example, if there are 5 page fragments in a taskflow, you could pass a parameter to the taskflow like 'Step3' which takes you to a specific page fragment. To do this, use a router as the default activity and execute navigation based on the parameter. You still have to deal with re-creating the state to display that page properly, and that depends on the application. Again, I could not understand what you meant and what you are trying to do here. How does an external application come to play in the middle of your taskflow ? Are you referring to the URL from where a user came to your application or a URL to your application getting sent to an external application ?

    ReplyDelete
  5. Hi,

    Nice post. I have the same requirement to bookmark the ADF application page. Downloaded sample application linked to this post.
    My Jdeveloper version is 11.1.1.7.0.
    Run the application & it is working fine i.e. showing departments & taking to the staff details page by clicking "Staff Details" button.
    But I am facing a problem here. I bookmarked the staff details page. Bookmark URL is as follows: localhost:7101/Bookmarking_with_ADF-ViewController-context-root/faces/staffDetails?_adf.ctrl-state=3pj0t13ib_19&dept=DEV&_afrLoop=26095716099107

    When I try to access this from another tab, it giving me "403 - Forbidden" error. If I remove the ADF state parameters (_adf.ctrl-state, _afrLoop) in the above URL then page loading fine.

    End user do not wish to remove ADF state parameters from the bookmarked URL. Then how to survive from this "403 - Forbidden" issue ?

    Thanks,
    Gopal.

    ReplyDelete
  6. Same issue as Gopal.
    Have any solution or workaround ???

    ReplyDelete
  7. Hi,

    I got a situation as follows:

    I got two Task flow created ( task flow1 and task flow2)
    and I have created a third task which I call the Introduction(task3), the jsff got just two buttons.
    when I click on button1 from task3, it should route to task flow1 and when click on button2 it should route to task flow2.
    Looks simple, i am new to ADF, plese help me here,

    Regards,
    Binu

    ReplyDelete
  8. Hi,

    My requirement is -
    1. get a parameter from URL and execute VOs based on that parameter. then display the results on page.
    2. I have follow an approach to create a Bounced task flow with default activity of method call. this method accepts parameter and executes VO. Then jsff activity is called which displays result.
    3. I have added this bounded task flow in jspx page as region and bookmarked it.

    when I run the jspx page, it keeps on reloading without showing any results in page. In logs i can see that parameter is read and VO is executed.

    When i downloaded your application and ran BoundedFlowContainer.jspx page, it was also behaving similar.

    Can you please help me understand what is my mistake. I am using JDeveloper 11.1.2.4

    Thanks,
    Kaushik

    ReplyDelete