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:
- The <af:setActionListener> copies the respective department name to a backing bean property
- Then the actionListener for the button fires executing the method binding,
- 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.
- This AM method in turn executes the StaffdetailsVO binding the given department name to it.
- 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)