Programmatic Navigation in WebCenter Portal application – do processAction from Java

3

Working on a WebCenter 11g Portal application, I recently ran into a challenge: when the user clicks a link in a task flow, the result of that action should be that the user is navigated to another page with a another taskflow that should display content based on context defined through the specific link that was clicked. The challenge was complicated by the fact that the taskflows had to be completely independent, of each other and of the page in which they were embedded.

The general approach with a taskflow that has a link that when clicked should result in effects outside the taskflow is to have the taskflow publish a contextual event with appropriate payload. It is then up to the page that embeds the taskflow in a region to consume and handle the event. That was the easy part.

The event handler can read the payload from the event, store values in a managed bean and navigate to the page that contains the drill-down-target-taskflow. This page has configured the input parameters for this second taskflow using EL expressions that refer to the managed bean that was populated by the event handler. Sounds straightforward, does it not?

What then is the catch in this story? It turned out to be not so straightforward to programmatically arrange for navigation to the specified page.

WebCenter does not work according to the standard JSF navigation model, but instead uses its own Navigation Models that contain pages and other node-types.

Image

Usually navigation is performed through the activation of an action component (command link, command button) that invokes the processAction operation on the Navigation Context.

<af:commandLink text="Return to Dashboard" id="cl12"
                actionListener="#{navigationContext.processAction}">
  <f:attribute name="node"
               value="#{navigationContext.navigationModel['modelPath=/oracle/webcenter/portalapp/navigations/programmaticNavigationModel'].
</af:commandLink>

However, I did not find any clear documentation on how to do navigation programmatically.

After a lot of trial and error and reading through a substantial number of OTN forum threads and blog articles, I put together the following contextual event handler that performs programmatic navigation:

public class PortalEventsHandler {

    private static final String RECORD_DETAILS_PAGE_EL = "#{navigationContext.navigationModel['modelPath=/oracle/webcenter/portalapp/navigations/programmaticNavigationModel'].node['p1']}";
    private static final String CURRENT_CE_CONTEXT_EL = "#{currentCEContext}";
    private static final String RECORD_ID_PAYLOAD_PARAMETER = "recordId";

    public void handleDrilldownEvent(Map payload) {
      // drill down needs to take us to page 1 (with id p1 in the programmatic navigation model)

      // set the selected recordId in the currentCECntext
      Integer recordId = (Integer)payload.get(RECORD_ID_PAYLOAD_PARAMETER);
      CurrentCEContext ceContext = (CurrentCEContext)JSFUtils.resolveExpression(CURRENT_CE_CONTEXT_EL);
      ceContext.setCurrentRecordId( recordId);

      // the processAction method that we need to use for navigation requires an ActionEvent
      // as input; this ActionEvent needs to have a Component as a source
      // this component should have an attribute called node that contains a node from a NavigationModel

      // 1. create the component to put into the Action Event
      Application application = FacesContext.getCurrentInstance().getApplication();
      HtmlCommandButton submitButton = (HtmlCommandButton)application.createComponent(HtmlCommandButton.COMPONENT_TYPE);
      // 2. find the page to navigate to - the nomination details page
      SiteStructureResource node = (SiteStructureResource)JSFUtils.resolveExpression(NOMINATION_DETAILS_PAGE_EL);
      // 3. create the ActionEvent and put the page node into it
      ActionEvent actionEvent = new ActionEvent(submitButton);
      actionEvent.getComponent().getAttributes().put("node", node);
      // 4. get hold of the NavigationContext to invoke the processAction on
      NavigationContext navContext = SiteStructureContext.getInstance();
      navContext.processAction(actionEvent);
    }

What is happening here is that on the fly an ActionEvent is created – since that is what the processAction method expects for an input. The ActionEvent is associated with a UIComponent – also created on the fly – because that is what ActionEvents are and because this component is the carrier of the node attribute that contains the node from Navigation Model to which navigation must be performed.

Share.

About Author

Lucas Jellema, active in IT (and with Oracle) since 1994. Oracle ACE Director for Fusion Middleware. Consultant, trainer and instructor on diverse areas including Oracle Database (SQL & PLSQL), Service Oriented Architecture, BPM, ADF, Java in various shapes and forms and many other things. Author of the Oracle Press book: Oracle SOA Suite 11g Handbook. Frequent presenter on conferences such as JavaOne, Oracle OpenWorld, ODTUG Kaleidoscope, Devoxx and OBUG. Presenter for Oracle University Celebrity specials.

3 Comments

  1. Ivo Leitão on

    Hi ! I’ve made the exact same thing (tnks a lot for your code) but nothing happens, it stays in the same page. In my case i’m reacting to a js286 event published by one of my portlets. The page that hosts the portlet catches that event (that is working fine) but when i use your code I’m not able to jump to other page. There is some kind of precondition to execute this code that i’m not aware of ?
    What I’m in doing a datacontrol is this:


    package vdf.myvdf.ui.portal.wc;

    import javax.el.ELContext;
    import javax.el.ExpressionFactory;
    import javax.el.ValueExpression;

    import javax.faces.application.Application;
    import javax.faces.component.html.HtmlCommandButton;
    import javax.faces.context.FacesContext;

    import javax.faces.event.ActionEvent;

    import oracle.adf.model.binding.DCBindingContainerValueChangeEvent;
    import oracle.adf.view.rich.context.AdfFacesContext;

    import oracle.webcenter.navigationframework.NavigationContext;
    import oracle.webcenter.portalframework.sitestructure.SiteStructureContext;
    import oracle.webcenter.portalframework.sitestructure.SiteStructureResource;

    public class EventHandler {
    public EventHandler() {
    super();
    }

    public void handleEventObjectPayload(DCBindingContainerValueChangeEvent customPayLoad) {
    String changedDepartmentName =
    (String)customPayLoad.getNewValue();
    handleEventStringPayload(changedDepartmentName);
    }

    public void handleEventStringPayload(String customPayLoad) {
    FacesContext facesCtx = FacesContext.getCurrentInstance();
    Application application = facesCtx.getApplication();
    ELContext elCtx = facesCtx.getELContext();
    ExpressionFactory expFactory = application.getExpressionFactory();

    ValueExpression ve = expFactory.createValueExpression(
    elCtx,
    "#{navigationContext.navigationModel['modelPath=/oracle/webcenter/portalapp/navigations/default-navigation-model'].node['home']}",
    Object.class);
    SiteStructureResource node = (SiteStructureResource)ve.getValue(elCtx);
    HtmlCommandButton submitButton = (HtmlCommandButton)application.createComponent(
    HtmlCommandButton.COMPONENT_TYPE);

    ActionEvent actionEvent = new ActionEvent(submitButton);
    actionEvent.getComponent().getAttributes().put("node", node);
    NavigationContext navContext = SiteStructureContext.getInstance();
    navContext.processAction(actionEvent);
    }
    }

  2. Zafar Siddiqi on

    Hi Lucas,

    Great article and a wonderful tip. However, the code snippet is missing some bits like the definition of NOMINATION_DETAILS_PAGE_EL.

    Would it be possible to put together a small sample around it and make available?

    Thanks
    Z.

Leave a Reply