In the first article of the Tomahawk series, I introduced MyFaces Tomahawk, one of the MyFaces component libraries that provide a set of stable components that can work with all the versions of JSF implementation (1.1, 1.2, and 2.0).

 

In this article, I will focus on the latest release of MyFaces Tomahawk (version 1.1.11) which is compatible with JSF 2.0. I will show you how to combine the power of MyFaces 2.0 core and Tomahawk in order to develop an interactive Mashup application which interacts with Google Maps.

 

Configuring Tomahawk in MyFaces 2.0 environment

The first thing you need to do is configure Tomahawk in MyFaces 2.0. Here are the steps:

      1.      Download the MyFaces Tomahawk binary distribution (tomahawk-1.1.11-bin.zip) from http://myfaces.apache.org/tomahawk/download.html

2.   Unzip the downloaded file.

3.   Copy lib/tomahawk-1.1.11.jar from the unzipped file to the WEB-INF/lib folder of your MyFaces 2.0 application.

4.   Install the MyFaces extensions filter by declaring it in the web.xml file of the web application (see listing 1). Make sure that the servlet-name value of the extension filter matches the name of the Faces servlet.

Listing 1. The MyFaces Extensions Filter in web.xml
											
<web-app>

  <filter>
    <filter-name>extensionsFilter</filter-name>
    <filter-class>org.apache.myfaces.webapp.filter.ExtensionsFilter</filter-class>
    <init-param>
      <description>Set the size limit for uploaded files.
                Format: 10 - 10 bytes
                        10k - 10 KB
                        10m - 10 MB
                        1g - 1 GB</description>
      <param-name>uploadMaxFileSize</param-name>
      <param-value>100m</param-value>
    </init-param>
    <init-param>
      <description>Set the threshold size - files
                    below this limit are stored in memory, files above
                    this limit are stored on disk.

                Format: 10 - 10 bytes
                        10k - 10 KB
                        10m - 10 MB
                        1g - 1 GB</description>
      <param-name>uploadThresholdSize</param-name>
      <param-value>100k</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>extensionsFilter</filter-name>
    <url-pattern>*.xhtml</url-pattern>
  </filter-mapping>
  <filter-mapping>
    <filter-name>extensionsFilter</filter-name>
    <url-pattern>/faces/*</url-pattern>
  </filter-mapping>

  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>/faces/*</url-pattern>
  </servlet-mapping>

</web-app>
										

  1. You must add the following declaration  (the line shown in bold in Listing 2) to each XHTML page that will be using Tomahawk:

Listing 2. Adding Tomahawk declaration to the html tag of the XHTML page
											
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
   xmlns:t="http://myfaces.apache.org/tomahawk">
										

Configuring GMaps4JSF

For the purpose of the Mashup application, we will use gmaps4jsf library for working with the maps. GMaps4JSF is the complete integration between JavaServer Faces and Google Maps. It offers many components in order to build declarative Google Maps in the JavaServer Faces world.

To configure GMaps4JSF with your JSF 2.0 Tomahawk application:

      1.      Download the GMaps4JSF binary distribution (gmaps4jsf-1.1.4.jar) from http://gmaps4jsf.googlecode.com/files/gmaps4jsf-1.1.4.jar

2.   Copy the jar to the WEB-INF/lib folder of your Tomahawk application.

  1. Add the following declaration (the line shown in bold in Listing 3) to your XHTML page:

Listing 2. Adding Tomahawk declaration to the html tag of the XHTML page
											
<web-app>

  <filter>
    <filter-name>extensionsFilter</filter-name>
    <filter-class>org.apache.myfaces.webapp.filter.ExtensionsFilter</filter-class>
    <init-param>
      <description>Set the size limit for uploaded files.
                Format: 10 - 10 bytes
                        10k - 10 KB
                        10m - 10 MB
                        1g - 1 GB</description>
      <param-name>uploadMaxFileSize</param-name>
      <param-value>100m</param-value>
    </init-param>
    <init-param>
      <description>Set the threshold size - files
                    below this limit are stored in memory, files above
                    this limit are stored on disk.

                Format: 10 - 10 bytes
                        10k - 10 KB
                        10m - 10 MB
                        1g - 1 GB</description>
      <param-name>uploadThresholdSize</param-name>
      <param-value>100k</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>extensionsFilter</filter-name>
    <url-pattern>*.xhtml</url-pattern>
  </filter-mapping>
  <filter-mapping>
    <filter-name>extensionsFilter</filter-name>
    <url-pattern>/faces/*</url-pattern>
  </filter-mapping>

  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>/faces/*</url-pattern>
  </servlet-mapping>

</web-app>
										

The Mashup Application

In the Mashup application, there is a master tree that contains different countries on different continents in the world. When the user selects a specific country from the tree, the country location is displayed in the detailed map as shown in figure 1.

To be able to implement the tree in Tomahawk, you can use the Tree2 component. Listings 4 and 5 show how to implement the tree; for this, we use the <t:tree2> tag, passing the TreeModelBase instance to its value attribute.

Listing 4. The Tree2 component ManagedBean
											
package beans;

import org.apache.myfaces.custom.tree2.TreeModelBase;
import org.apache.myfaces.custom.tree2.TreeNode;
import org.apache.myfaces.custom.tree2.TreeNodeBase;

@ManagedBean(name="tree")
public class Tree2Bean {
	
	private TreeModelBase treeModelBase;
	
	public Tree2Bean() {
	
		// create base nodes.
		TreeNode continentNode = createNode(Tree2Constants.WORLD_FACET_NAME,
				"Continents", false);
		TreeNode africaNode = createNode(Tree2Constants.CONTINENT_FACET_NAME,
				"Africa", false);
		TreeNode asiaNode = createNode(Tree2Constants.CONTINENT_FACET_NAME,
				"Asia", false);
		TreeNode europeNode = createNode(Tree2Constants.CONTINENT_FACET_NAME,
				"Europe", false);

		// add continents.
		addNodeToTree(africaNode, continentNode);
		addNodeToTree(asiaNode, continentNode);
		addNodeToTree(europeNode, continentNode);		
		
		// add Asian countries.
		addNodeToTree(createNode(Tree2Constants.COUNTRY_FACET_NAME, "Japan",
				true), asiaNode);
		addNodeToTree(createNode(Tree2Constants.COUNTRY_FACET_NAME, "China",
				true), asiaNode);
		addNodeToTree(createNode(Tree2Constants.COUNTRY_FACET_NAME, "Iran",
				true), asiaNode);

		// add African countries
		addNodeToTree(createNode(Tree2Constants.COUNTRY_FACET_NAME, "Egypt",
				true), africaNode);
		addNodeToTree(createNode(Tree2Constants.COUNTRY_FACET_NAME, "Mali",
				true), africaNode);
		addNodeToTree(createNode(Tree2Constants.COUNTRY_FACET_NAME, "Sudan",
				true), africaNode);

		// add European countries
		addNodeToTree(createNode(Tree2Constants.COUNTRY_FACET_NAME, "Austria",
				true), europeNode);
		addNodeToTree(createNode(Tree2Constants.COUNTRY_FACET_NAME, "England",
				true), europeNode);
		addNodeToTree(createNode(Tree2Constants.COUNTRY_FACET_NAME, "Germany",
				true), europeNode);		
		
		// Update the treeModelBase
		treeModelBase = new TreeModelBase(continentNode);
	}

	/*
	 * This addNodeToTree() static method is used for adding a tree node
	 * (childNode) to a parent tree node (parentNode).
	 */
	private static void addNodeToTree(TreeNode childNode, TreeNode parentNode) {
		parentNode.getChildren().add(childNode);
	}

	/*
	 * This createNode() static method is used for creating a tree node.
	 */
	private static TreeNode createNode(String facetName, String nodeText, boolean isLeaf) {

		return new TreeNodeBase(facetName, nodeText, isLeaf);
	}	

	/**
	 * The getData() method is used for returning the TreeModel that would be
	 * used by the tree component value attribute.
	 *
	 * @return TreeModel
	 */
	public TreeModelBase getData() {
        		return treeModelBase;
	}
}
										

The TreeModelBase instance is actually a wrapper of a tree of TreeNode instances. To create an instance of the TreeNode class, the following information is needed:

  1. type The node type that should match the facet name indicated in the view (the XHTML page).
  2. description The node text.
  3. isLeaf Determines whether the node is a leaf.

                        

 

Now, let's move to the view page. Listing 5 shows the complete mashup application XHTML view page which includes the <t:tree2> component.

Listing 5. The Mashup application XHTML view page
											
<t:tree2 id="clientTree" value="#{tree.data}" var="node"
	varNodeToggler="t">

	<f:facet name="world">
		<h:panelGroup>
			<t:graphicImage value="../images/myfaces.gif" border="0" />
			<h:outputText value="#{node.description}" styleClass="nodeFolder"
/>
		</h:panelGroup>
	</f:facet>

	<f:facet name="continent">
		<h:panelGroup>
			<f:facet name="expand">
				<t:graphicImage value="../images/yellow-folder-open.png"
				  rendered="#{t.nodeExpanded}" border="0" />
			</f:facet>
			<f:facet name="collapse">
				<t:graphicImage value="../images/yellow-folder-closed.png"
				  rendered="#{!t.nodeExpanded}" border="0" />
			</f:facet>
			<h:outputText value="#{node.description}" styleClass="nodeFolder"
/>
		</h:panelGroup>
	</f:facet>

	<f:facet name="country">
		<h:panelGroup>
			<h:commandLink immediate="true"
styleClass="#{t.nodeSelected ? 'documentSelected':'document'}"
				actionListener="#{t.setNodeSelected}">
				
				<t:graphicImage value="../images/document.png"
border="0" />
				<h:outputText value="#{node.description}" />				
				<f:param name="countryName"
value="#{node.description}" />

<f:ajax execute="clientTree" render=":mashupForm:output" />								
			</h:commandLink>
		</h:panelGroup>
	</f:facet>

</t:tree2>

…
<h:panelGroup id="output">
	<h:outputText id="countryName" value="You selected : #{param.countryName}"
rendered="#{param.countryName ne null}"/>
			
	<m:map id="map" width="500px" height="450px" type="G_HYBRID_MAP"
		   address="#{param.countryName}" enableScrollWheelZoom="true"
		   partiallyTriggered="true" zoom="4"
		   rendered="#{param.countryName ne null}">
			
		 <m:marker>
			 <m:htmlInformationWindow htmlText="#{param.countryName}" />
		 </m:marker>
	 </m:map>		
</h:panelGroup>
										

In Tomahawk, The tree levels are represented by facets. The application tree has three levels represented by three facets: a world facet representing the root node, a continent facet representing the continent nodes, and a country facet representing the country nodes. You can customize the view of every level when it is expanded and collapsed by implementing the child expand and collapse facets. In Listing 5, the continent facet will display two different images depending on whether the node is expanded or collapsed.

 

Tomahawk supports two types of trees: server side and client side. To be able to implement a server-side tree, you need to set the clientSideToggle attribute to false. The main difference between the server and the client side toggled trees is that in the server-side toggled tree, only the visible part of the tree is sent to the client, while in case of the client side toggled tree, the entire tree is returned to the client. In our example, we are using the client side tree because it has more user friendly navigation.

 

The country facet has a command link that is used for passing the country name (node description) as a parameter when the node is clicked. The parameter is then used to display the selected country name and location in the map.

 

Thanks to the Tomahawk JSF 2.0 support, the JSF's Ajax features now work fine with the components. Here, we're using the <f:ajax> tag inside the command link of the country facet. It executes the tree2 component action and then updates the panelGroup, which holds the map component and the label with the new selected country.

 

GMaps4JSF offers a declarative way for working with maps inside the JSF world. Here, the new country name comes from the server, the map component is updated with the new country name specified in the address attribute, and a marker with an information window is attached to the new location as shown in Figure 1 (for more information about GMaps4JSF, check the documentation here).

 

Conclusion

In this article, you learnt how to use the MyFaces Tomahawk component library in the JSF v2 world. You also know how to integrate Tomahawk with GMaps4JSF as a powerful Google Maps JSF integration library.

Finally, I would like to say that MyFaces Tomahawk is still alive. Thanks to the MyFaces community, Tomahawk is supporting the JSF 2.0 powerful features. This sort of support adds an extra strength for this mature library for building more robust and richer Web 2.0 applications.