Take Advantage of Your Site Map with Navigation API in Red Hat JBoss Portal 6.1

October 14, 2013

Portal

It’s All About Navigation

Today’s web application often have to provide easy navigation user experiences to the customers to provide the latest and greatest to be accessible by the customer.

For example, a website may want to offer rich, customized menu navigation rather than the out-of-the-box menu portlet or site map portlets. More often than not, the menu may need to visually indicate the current page that it’s on.

New Navigation API allows developers access Portal Site Map, so developers can create rich, customized navigation menu.

New Navigation API allows developers access Portal Site Map, so developers can create rich, customized navigation menu.

Another commonly seen usages of having a complete site map access is to be able to construct sub-navigation and/or trace-back / breadcrum navigation menus. For example, if “My Awesome Product” is navigable from the menu items: Product > Awesome Product Category > My Awesome Product, then you may want to display a custom bread crumb navigation in that format!

With Navigation API, developers can also use it to build custom bread crumbs allowing single click navigation.

With Navigation API, developers can also use it to build custom bread crumbs allowing single click navigation.

On the other hand, the portlet may need to dynamically create brand new menu items or removing existing menu items on the fly! For example, a Content Management System may want to create a new item in the Site Map (and thus appearing in the menu) when a user creates new content.

It’s now possible to do all of the above with JBoss Portal 6.1’s Navigation API.

Basics of Navigation API

All of the Site Map (Navigation) information is stored inside Portal’s Java Content Repository (JCR). To make this data easily accessible, JBoss Portal 6.1 is introducing Navigation API. Navigation API shelters developers from mundane details of JCR and underlying data relationships, but at the same time, provides developers powerful features to access, and manipulate Navigation data (i.e., the Site Map).

The entry point of Navigation API is PortalRequest > Navigation:

// To access the navigation for the current request
PortalRequest portalRequest = PortaRequest.getInstance();
Navigation navigation = portalRequest.getNavigation();

// To access navigation for a specific Site or Group
Portal portal = portalRequest.getPortal();
Navigation adminNav = portal.getNavigation(new SiteId(new Group("platform", "administrators"));

You can find more detailed information via the community API documentation, as well as community forum.

It’s very important to remember that Navigation API mostly deals with the Node entity. Portal’s Site Map / Menu items are simply a hierarchy of Nodes. A Navigation Node can be thought as a single menu item in the Navigation Menu, or the Site Map. Every node has a Name, a Display Name – and each node may be belonging to a Parent, or containing Children.

Node’s Name is usually a short string that can be used as part of the URI when navigating to the page. For example, Contact Us node may have a Name of “contact-us”, and its Display Name can be “Contact Us”. When constructing the URI, it would become something like:

http://myportal/portal/classic/contact-us 
Under the default root node, every menu item in your site is a Node object.

Under the default root node, every menu item in your site is a Node object.

In the above diagram, the top level nodes are Home, News, Career, Services, and Contact Us. They all have an implicit parent known as the Root node. Services node has children nodes of Consulting, and Evaluation.

Last, if not least, it’s also important to remember that a Page object is separate from the Navigation Node object. A Page object in Portal contains the portlet instances that belongs to the page, and the physical layout of the page. A Navigation Node simply references to the Page object. So, for example, when you create a Contact Us page, and associated a 2-column layout, and dropped in a Contact Us portlet – a Page object was created to store that information, and subsequently, a corresponding Navigation Node object was created so that it will appear in the Site Map and Navigation Menu.

What that also means is that multiple Navigation Nodes can reference to the same Page object, so that the page can be reused in your Site Map.

Accessing a Node

Now that the basic concepts are out of the way, let’s access a Node. Every Portal Site has an implicit Root node. You can get to the Root fairly easily by:

Node root = navigation.getNode(NodePath.root());

Of course, most of the time, you may know the node you want to access. Using the earlier sample diagram, to access the Home node, you can do:

Node home = navigation.getNode("home");

You can also retrieve any child node by providing a full path, either by string, or by NodePath object:

Node evaluation = navigation.getNode("services", "evaluation");
// or...
Node evaluation = navigation.getNode(NodePath.fromString("/services/evaluation");

As you can see from above snippet, every node is accessible via a path – you can think of it as a file path in the file system in order to locate a file. In JBoss Portal, you can locate a Node by giving a NodePath.  NodePath uses slash “/” as the path separator.

Once a node is retrieved, you can get its NameDisplay Name, or the Page ID of the referenced Page object. Simply retrieving a single node does not let you access or modify its children nodes!

Let’s see how we can access the children by using NodeVisitor in the next section.

Traversing the Site Map

So if loading a node does not allow you to access its children or descendants, then how do you build your navigation menu? A different question to ask is – why doesn’t JBoss Portal load all of the children nodes and descendants by default in the first place?

The reason is that customers may have a complex navigation / Site Map with hundreds of Nodes organized into sophisticated hierarchy. If JBoss Portal always loads all children or descendants, then it may be spending precious resources when those descendant’s aren’t even being used.

Hence, the Navigation API allows the developer to specify when to load what by implementing the Visitor pattern. A NodeVisitor simply tells JBoss Portal whether to load a given node by returning yes or no (true, or false).

public class MyVisitor implements NodeVisitor {
    public boolean visit(int depth, String name, NodeDetails details) {
        // return whether to visit node or not
    }
}

To avoid writing common visitors, Navigation API provides a couple frequently used, and helpful, NodeVisitor implementations:

// Return Services node with children preloaded
Node services = navigation.getNode(NodePath.path("services"), Nodes.visitChildren());
// Return EVERYTHING in the Site Map.
Node root = navigation.getRoot(Nodes.visitAll());

Lastly, before trying to access a Node’s child node, you may want to make sure the children were loaded:

if (!node.isChildrenLoaded()) {
    navigation.loadChildren(node);
}

Knowing all these, building a complete Site Map becomes trivial. In addition, building custom menu with sophisticated lazy loading mechanism is also extremely easy. You can, for example, expose the entire Site Map via a custom REST service so that you can build dynamic JavaScript based navigation  user interface!

Where Am I?

If you want to build a bread crumb based on where the user is visiting, or let the user see immediate sub-pages of the current page, you first need to know which Navigation Node the user is currently on. It’s important to remember that, whenever thinking in terms of what page the user is visiting – it’s determined by the URI, and then mapped to the Navigation Node. The Node then references a Page object.

To determine which node the user is currently on, you can do:

NodePath currentPath = portalRequest.getNodePath();
Node node = navigation.getNode(currentPath);

And from there, you can traverse upwards by using node.getParent(), or downwards by using the Visitor pattern. Easy, right?

In the MDBlog sample portlet, the portlet will display a list of sub-pages under the current Blog. The actual code can be found in MDBlogPortlet.java. Similarly, MDBlog posts blog links directly onto Social Media network – the link is constructed by using the Navigation API!

String link = config.getBaseUrl() + portalRequest.getURIResolver()
  .resolveURI(portalRequest.getSiteId()) + portalRequest.getNodePath();

Manipulating the Site Map

So how do you actually make changes to the Site Map from your portlet? With Navigation API, you can actually create, update, and delete new Navigation Nodes as well.

Deleting a Node

Deleting a node is easy. You must do this from the parent node. For example, to delete Consulting item from Services, you can do:

Node services = navigation.getNode("services");
services.removeChild(”consulting");
navigation.saveNode(services);

in MDBlog, you can delete a Blog directly from the portlet and it’ll also remove the Blog from navigation menu and Site Map.

Updating a Node

With any node, you can modify its Name, or Display Name, or the Page it should reference to:

Node services = navigation.getNode("services");
services.setDisplayName("Awesome Services");
navigation.saveNode(services);

Creating a Node

Lastly, your portlet can also create a new Navigation Node, thus providing your users with seamless portal experience. For example, in MDBlog, when creating a new Blog, it’ll create a new Navigation Node for the new Blog as well so that it will appear in the Navigation Menu as well as the Site Map!

To create a new node:

// It's important to start from the parent, with children loaded
Node parent = navigation.getNode(NodePath.path(...), Nodes.visitChildren());
Node newChild = parent.add("name");
newChild.setDisplayName("My New Item");
// You need to have a PageId reference beforehand
newChild.setPageId(pageId);
// Lastly, you save the parent (not the child node)!  This will implicitly save the child node
navigation.save(parent);

Whew!

That was a lot of material to cover in a single article. This article also concludes our long 6-part series to introduce JBoss Portal 6.1 features! I truly hope that when you have a good understanding of the features, seeing valuable use cases, and also seeing the actual code samples, will help you design better end-user features when using JBoss Portal 6.1.

About Ray Tsang

Ray has worked with technology all his career. Ray was a manager at a global consulting company before taking time off to backpack around Asia. Ray joined Red Hat at the beginning of 2012 as a Solution Architect focusing on Red Hat Middleware products including JBoss Data Grid and JBoss Portal Platform.

View all posts by Ray Tsang

No comments yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 115 other followers

%d bloggers like this: