Friday, April 22, 2011

How to add a context menu in a table


In ADF11 it is possible to create context menus in a table. This is a menu that can be opened by right clicking on a table row.

In this blog is described how to add a context menu in a table.

Overview of the context menu:



Setup example application



For this blog an example application is created based on the HR schema. The example application contains an employees table with 2 detail forms, one to insert and edit an employee without department) and 1 to change the department of the employee. The detail forms are created in separate pages. For the employees table and forms a bounded task flow is created this bounded task flow is started from the menu.


Model layer



Create the following entities:
Entity nameBased on table of HR schemaCustomizations made
EmployeeEMPLOYEESNone


Create the following view objects:
View object nameBased on entitiesCustomizations made
EmployeesViewEmployeeNone


Create an application module HrAppModule which exposes the EmployeesView.



Task flow




Unbounded task flow



The unbounded task flow form where we start with the solution looks like this:



There are no customizations made, the task flow is created by drag and drop.

Bounded task flow



The employees task flow is a bounded task flows:



The next properties are set:
PropertyValue
usePageFragmentsfalse

Share data controls with calling task flowtrue




Table page



The table pages are created by drag and drop from the Data Controls. The table is dropped as ADF Read-only table with Row Selection and Sorting checked, all columns are displayed.





The Row Selection property must be checked, this causes the following properties to be set in the table:
PropertyValue
selectedRowKeys#{bindings.EmployeesView.collectionModel.selectedRow}
selectionListener#{bindings.EmployeesView.collectionModel.makeCurrent}
rowSelectionsingle


Although in the JSPX page the selectedRowKeys and selectionListener statements contains warnings that the references methods cannot be found they can be found runtime.

Form pages



The form pages are created by drag and drop from the Data Controls. The form is dropped as ADF Form.



To navigate back from the form to the table a rollback button (af:commandButton) is added by drag and drop the Rollback operation from the datacontrol palette as a button.



The submit button is created in the same way but then from the Commit operation.

The edit and insert employee page contains all fields but with department ID readOnly. The change department page contains the employee ID, first name, last name and department ID. All fields except department ID are read only.

Add the context menu



An ADF table contains a facet named contextMenu. With this facet a menu popup can be created that pops up when the user right clicks on a table row. If the table contains no rows the context menu does not popup on right click.

Insert in the table the contextMenu facet. This facet should contain a popup and in this popup a menu can be defined. Submenu entries can be created by inserting another menu tag in the menu:
<f:facet name="contextMenu">

<af:popup id="popup">

<af:menu id="menu">

<af:menu id="employeeMenu"

text="Employee">

<af:commandMenuItem text="Edit" 

id="editEmployeeMenu"

action="edit"/>

<af:commandMenuItem text="Insert"

id="insertEmployeeMenu"

action="edit"

actionListener="#{bindings.CreateInsert.execute}"/>

</af:menu>

<af:menu id="departmentMenu"

text="Department">

<af:commandMenuItem text="Change department for employee"

id="changeMenu"

action="change"/>

</af:menu>

<af:commandMenuItem text="Cancel"

id="cancelMenu"

action="cancel"/>

</af:menu>

</af:popup>

</f:facet>
The insert menu option is created by drag and drop the CreateInsert operation of the EmployeesView, the action is overridden so it navigates to the same page as edit employee:



When the user for example choses Change department for employee:



This results in:

Dynamic Buttons in Oracle ADF


Standard way to create buttons on the page, is to use drag-and-drop method and JDeveloper wizards. However, sometimes its not enough, especially when there are requirements to apply dynamic rules based on security roles or page groups. With dynamic buttons generation, you can use XML configuration files, this will allow to change displayed buttons and properties right on runtime. I'm writing this post, based on influence I got from Frank Nimphius post - ADF Faces RC - Implementation strategies for global buttons in page templates. Frank describes fundamental approach for global buttons, I'm in my post focusing on dynamic global buttons aspect.


You can download developed application - GlobalDynamicButtons.zip. This sample is based on two Entity objects - Countries and Locations, and provides two JSPX pages based on template JSPX. Button components for both pages are generated dynamically. Here is a navigation flow implemented in current sample:


Both of the pages are based on template. This template defines facet for data-bound components and empty toolbox for dynamically generated buttons:


You can notice, that toolbox contains two toolbar components, button objects will be added to those toolbars. Toolbox is binded to managed bean, this allows to initialize toolbox component on the page:


Next step is to bind BeforePhase property of f:view tag contained on the pages to the same managed bean where dynamic buttons are generated:


#{dynamicGenerator.addButtons} method generates dynamic buttons according to current page, information about current page is accessed through phaseEvent, for example:


Buttons are binded to actions defined in actual page definition using ActionListener, disabled property is also set programatically:


If button is not binded to page definition, and is is related to navigation flow, action expression is set:


Main advantage of described approach is that it allows to have the same code for the same actions in different pages - Commit action, for example. And only what you need to care about, is to define actions in page definition files. Actions for countries page:


Actions for locations page:


On runtime, all buttons specific to current page are generated:


First and Previous buttons are disabled, since first entry is shown, Locations button opens locations from current country:


Different set of buttons is generated and by default there are no locations defined for Argentina, we can create a couple:


When Save button is pressed, records are stored to database and sequence values for LocationIdcolumn are assigned:


Wednesday, April 20, 2011

Web Services Interoperability between Oracle WebLogic Server 11g and Microsoft.NET WCF 4.0


Recently I was asked to demonstrate the interoperability in a real life scenario between Oracle WebLogic Server 11g and Microsoft.NET Windows Communication Foundation 4.0 using secure web services. 


After a bit of research, I have found this very helpful article by Juan Carlos.

I will try to extend the above article and provide full step by step instructions on how to create a secure web service, deploy it on your integrated WebLogic Server and use Microsoft.NET WCF 4.0 and C# to consume it.

So let's start with developing the web service.


1. In the Applications Navigator, click New Application. Give your application a name (for example HelloWorldApp) and select Generic Application from the application templates. Click next.


2. Specify a project name (for example HelloWorldPrj) and click finish.


3. Create a new Java class in the newly created project by specifying a class name (HelloWorld), a package name (demo) and uncheck "Constructors from Superclass" and "Implement Abstract Methods" and click OK.


4. Copy and paste the following code below in your HelloWorld class.

public String sayHello(String name){
return "Hello " + name;
}


5. Once you have done this, click save all and right-click the HelloWorld.java class. From the menu, select "Create Web Service ...".


6. Select "Java EE 1.5, with support for JAX-WS Annotations" for the deployment platform and click next.


7. In the "Generation Options" step leave the defaults and click next.


8. In step 4 of the wizard, select SOAP 1.2 Binding and click next.


9. In steps 5 and 6 click next. In step 7, "Configure Policies", select "OWSM Policies" and "oracle/wss_username_token_service_policy" check box in the security policies and click finish.


10. Right-click the HelloWorld.java class and choose run. By doing so, the integrated WebLogic Server will start and the HelloWorld web service will be deployed to the server.


11. If you have followed my naming suggestions for the project and class name, you should see in the log window of the WebLogic Server a target url, http://localhost:7101/HelloWorldApp-HelloWorldPrj-context-root/HelloWorldSoap12. This is the actual web service url and JDeveloper provides you with a web service test client utility. Click on the link to test the web service.


12. In the HTTP Analyzer window, specify an input value. Make sure that you expand SOAP Headers, WS-Security:Header and check the include check box. In the username and password specify weblogic and weblogic1 respectively. If you do not include the security header, then you will get "InvalidSecurity : error in processing the WS-Security security header". Click Send Request.


13. You should see the reply back from the web service that we have developed and deployed on our WebLogic Server.


14. Before proceeding with creating a client to consume the web service, please copy the wsdl url (http://localhost:7101/HelloWorldApp-HelloWorldPrj-context-root/HelloWorldSoap12HttpPort?WSDL), as we will need it to create the proxy class from Visual Studio.


15. For creating the proxy class from our wsdl file, we will beusing the ServiceModel Metadata Utitlity Tool, called scvutil.exe.The ServiceModel Metadata Utility tool is used to generate service model code from metadata documents and metadata documents from service model code. So navigate to "C:\Program Files\Microsoft SDKs\Windows" and you should see one or more version folders. Open the latest folder (in my case i only have one, v7.0A) and then bin (the complete path is "C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin") and copy the path.


16. Right-click "Computer" and select "Properties". Click "Advanced System Settings" and select the "Advanced" tab. Then click "Environment variables".


17. Under "System variables", select the "Path" variable. Go to the end of the variable value and after including a semicolon (;), paste the ServiceModel Metadata Utility tool full path (C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin)


18. Click "OK" on the "Edit System Variable", "Environment Variables" and "System Properties" to save and dismiss all three windows. Open a command prompt and navigate to your desired location (in my case I have created a folder called demo on my desktop). Write the following command, "svcutil , to create the proxy class from the wsdl file. Please note to replace the with your wsdl url location. In my case it is "http://localhost:7101/HelloWorldApp-HelloWorldPrj-context-root/HelloWorldSoap12HttpPort?WSDL". If you run this command as it is, it will create a C# proxy class for the web service specified in the wsdl and a configuration file. if you want to generate the class in another language, svcutil provides you with a parameter called "language" where you can specify the language code to be generated (for example, you can append after the url location /language:VB to generate the class in visual basic). 


19. The svcutil utility will generate two files, HelloWorldService.cs which is the C# proxy class and the output.config file, which is the configuration file. If you see warnings about policy assertions not being imported, don't worry, we will handle the security headers later.


20. Open Microsoft Visual Studio and create a new project.


21. From the Installed Templates, expand Visual C# and select Windows.Select Console Application, give your project a name (for example, HelloWorldVS) and click Ok.


22. Before proceeding and importing the two files generated (HelloWorldService.cs and output.config), we need to add to our project references to two assemblies for our solution to work. So right-click References and click "Add Reference".


23. From the "Add Reference" window, select the .NET tab and select "System.Runtime.Serialization" and "System.Service.Model" and click OK.


24. Next, right-click your project name (HelloWorldVS) and select Add -> Existing Item from the menu.


25. Navigate to the folder where your proxy and configuration file is (generated by svcutil) and make sure that the file filter is set to "All files (*.*)" so that both files (HelloWorldService.cs and output.config) are visible. Select both files and click "Add".


26. You should see the two files now in your project. Open the Program.cs file and in the main method, copy and paste the following code:

HelloWorldClient client = new HelloWorldClient();
String response = client.sayHello("Antonis Antoniou");
Console.WriteLine(response);


27. If you attempt to run the application (F5), you should see "Could not find default endpoint element that references contract 'HelloWorld' in the ServiceModel client configuration section.". This is because it is missing an application configuration file. Although we have imported the configuration file generated by svcutil, it is not recognized.


28. A simple trick to overcome this issue is to rename the output.config file to app.config. So if you rename the configuraiton file to app.config and run the application, the above error should disappear. However, a new error arises, "The CustomBinding on the ServiceEndpoint with contract 'HelloWorld' lacks a TransportBindingElement. Every binding must have at least one binding element that derives from TransportBindingElement.".


29. What this basically means is that because in our configuration file, a custom binding is specified, we need to define a transport protocol (for example, http, https). To do so, open the app.config file and just after the closing textMessageEncoding element, add the following:


30. If you run the application again, the TransportBindingElement error should be resolved. However now we have a new error, "InvalidSecurity : error in processing the WS-Security security header". As the error indicates, a security header is expected by our HelloWorld web service and we have not provided one.


31. The easiest way to provide this security header along with the username and password required to run the web service is again though the app.config configuration file. So open the app.config file and within the endpoint element add the security headers xml tags as shown in the image below.


32. Run the application again (Ctrl + F5). You should see the return statement from the HelloWorld web service

Tuesday, April 19, 2011

ADF 11g Programmatic partial page refresh (PPR) with dynamic regions


When working with bounded taskflows it is important to have the ability to interchange them on the page. Unfortunately when you do this you also have to call that section of the page to refresh the information.

In other words, you should call a Programmatic partial trigger in the backing bean of your application on a dynamic region on your main page.
Lets get started and create a new Project:

Delete the model project as you wont need it for this.
In your remaining view controller project create a JSF page with a stretch layout

Then create 3 Taskflows. These are going to be the menu and 2 pages that we are going to switch between when a menu items are clicked.
> menu-task-flow-definition.xml
> page1-task-flow-definition.xml
> page2-task-flow-definition.xml

Inside the each of the taskflows add a “view” component from the component pallet and then right clicked it selecting “Create Page Fragment”. Create a page fragment for each taskflow respective of it’s name.

Lets make a start on the menu page fragment and add a couple of buttons to be uses to change the page. We’ll be adding this taskflow to the left of our JSF page. When an item is clicked the Center panel is updated with the selected page. Open your two other pages and stick on a panel header with “Page1″, “Page2″ respectively or something that shows each page is different.

Next we’ll do some work on the JSF page. open your “adfc-config.xml” file (unbounded Taskflow). As the JSF isn’t a taskflow we’ll add this here. You might later need to have some kind of action for this page so it’s good practice to add this here.

Now we’ll add the “Start” facet to the JSF page. This is where we’ll put the menu item.

Drag the menu Taskflow from the Application Navigator into the start facet and select “Region” from the Create menu.

Next drag one of the page Taskflows onto the “Center” facet and select “Dynamic Region”. You will then be prompted to create a Managed bean. Create one.

Make a not of the ID of your dynamic region

This next bit is a little fiddly and I think there might well be a better way of doing this with adding the bean to the other pages. However this is the way I’ve gone about it. We’re now going to widen the scope of your backing bean to an application managed bean. Open the “adfc-cong.xml” and select “Managed Beans” change the scope of the bean we just created to Application.

Now that you’ve done this the binding created on the JSF page will be invalidated. Open the JSF and go to the “bindings”. Double click the dynamic taskflow. Removing the “backingbeanscope” part of the bind variable.

Now lets look at the Managed bean we created. In here we have the programmatic PPR. The 2 methods for changing the Taskflows will refresh the region. I’ve used a couple of methods for finding the region which are taken from other examples found on the web. You’ll need to put the name of the region on your JSF page into each of these methods.
package com.demo.vc.regions;
 
import java.util.Iterator;
 
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
 
import oracle.adf.controller.TaskFlowId;
import oracle.adf.view.rich.context.AdfFacesContext;
 
public class CenterRegion {
 
    private String taskFlowId1 = "/WEB-INF/page1-task-flow-definition.xml#page1-task-flow-definition";
    private String taskFlowId2 = "/WEB-INF/page2-task-flow-definition.xml#page2-task-flow-definition";
 
    //Default taskflow
    private String taskFlowId = taskFlowId1;
 
    public CenterRegion() {
    }
 
    public TaskFlowId getDynamicTaskFlowId() {
        return TaskFlowId.parse(taskFlowId);
    }
 
    //Called by the menu taskflow
    public String showPage1() {
        taskFlowId = taskFlowId1;
        AdfFacesContext.getCurrentInstance().addPartialTarget(
                                findComponentInRoot("r2"));
        return null;
    }
    //Called by the menu taskflow
    public String showPage2() {
        taskFlowId = taskFlowId2;
        AdfFacesContext.getCurrentInstance().addPartialTarget(
                                findComponentInRoot("r2"));
        return null;
    }
 
    /**
     * Locate an UIComponent in view root with its component id. Use a recursive way to achieve this.
     * @param id UIComponent id
     * @return UIComponent object
     */
    public static UIComponent findComponentInRoot(String id) {
        UIComponent component = null;
        FacesContext facesContext = FacesContext.getCurrentInstance();
        if (facesContext != null) {
            UIComponent root = facesContext.getViewRoot();
            component = findComponent(root, id);
        }
        return component;
    }
 
    /**
     * Locate an UIComponent from its root component.
     * Taken from http://www.jroller.com/page/mert?entry=how_to_find_a_uicomponent
     * @param base root Component (parent)
     * @param id UIComponent id
     * @return UIComponent object
     */
    public static UIComponent findComponent(UIComponent base, String id) {
        if (id.equals(base.getId()))
            return base;
 
        UIComponent children = null;
        UIComponent result = null;
        Iterator childrens = base.getFacetsAndChildren();
        while (childrens.hasNext() &amp;&amp; (result == null)) {
            children = (UIComponent)childrens.next();
            if (id.equals(children.getId())) {
                result = children;
                break;
            }
            result = findComponent(children, id);
            if (result != null) {
                break;
            }
        }
        return result;
    }
}
With that done we now need to called the two methods from our menu Taskflow. Open the taskflow and put two “Method Call” items onto the taskflow. Then add two control flow cases going from the menu page fragment view to each of the respective methods. Also it’s wise to add a wildcard with a return flow case.

Change the “Fixed Outcome” of each of the method calls to the name of the flow case. And then add the method for changing the page from the Managed bean.

Finally we need to add the actions to each of our buttons on the Menu page fragment

Run the application and watch the magic of the page change when you click the button.
Download the project here: DynamicRegion