<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ include file="/common/taglibs.jsp"%>
<%@ page import="java.util.Map,
javax.servlet.jsp.jstl.sql.Result,
net.sf.navigator.menu.MenuComponent,
net.sf.navigator.menu.MenuRepository"%>
<html>
<head>
<title>Dynamic, Database-driven Menu</title>
<link rel="stylesheet" type="text/css" media="all"
href="<c:url value="/styles/default.css"/>" />
<link rel="stylesheet" type="text/css" media="all"
href="<c:url value="/styles/menuExpandable.css"/>" />
<script type="text/javascript"
src="<c:url value="/scripts/global.js"/>"></script>
<script type="text/javascript"
src="<c:url value="/scripts/menuExpandable.js"/>"></script>
</head>
<body>
<div id="header"></div>
<div id="content">
<h1>Database Driven Menu</h1>
<p>This page is designed to show how easy it is create menus from a couple
of database tables. I'm using JSTL's SQL Tags because they're easy and you
can see all the code that's needed in this one page. This example creates
tables, populates them and then extracts the values to build a menu. This
logic should probably go into a Servlet, Struts Action or a Servlet Filter to
get the logic of who sees what out of the view. But this works, so feel free
to copy and/or improve.
</p>
<sql:transaction dataSource="jdbc/appfuse">
<sql:update>
DROP TABLE IF EXISTS menu_item
</sql:update>
<sql:update>
CREATE TABLE menu_item (
id BIGINT not null,
parent_name VARCHAR(30),
name VARCHAR(30),
title VARCHAR(30),
description VARCHAR(50),
location VARCHAR(255),
target VARCHAR(10),
onclick VARCHAR(100),
onmouseover VARCHAR(100),
onmouseout VARCHAR(100),
image VARCHAR(50),
altImage VARCHAR(30),
tooltip VARCHAR(100),
roles VARCHAR(100),
page VARCHAR(255),
width VARCHAR(5),
height VARCHAR(5),
forward VARCHAR(50),
action VARCHAR(50),
primary key (id)
)
</sql:update>
<sql:update var="updateCount">
INSERT INTO menu_item
(id, name, title)
VALUES
(1,'DatabaseMenu','Database Menu')
</sql:update>
<sql:update var="updateCount">
INSERT INTO menu_item
(id, parent_name, name, title, location)
VALUES
(2,'DatabaseMenu','Yahoo','Yahoo Mail','http://mail.yahoo.com')
</sql:update>
<sql:update var="updateCount">
INSERT INTO menu_item
(id, parent_name, name, title, location)
VALUES
(3,'DatabaseMenu','JavaBlogs','JavaBlogs','http://javablogs.com')
</sql:update>
<sql:update var="updateCount">
INSERT INTO menu_item
(id, name, title, location)
VALUES
(4,'StandaloneMenu','Standalone Menu','http://raibledesigns.com')
</sql:update>
<sql:query var="menus">
SELECT * FROM menu_item order by id;
</sql:query>
</sql:transaction>
<p>
<strong>DONE:</strong> successfully created menu_items table and
added the following entries.
</p>
<display:table name="${menus.rows}" class="list" style="width: 600px">
<display:column property="id"/>
<display:column property="name"/>
<display:column property="parent_name" title="Parent Name"/>
<display:column property="title"/>
<display:column property="location"/>
</display:table>
<p>
If you <a href="#" onclick="toggleDisplay('sqlSource'); return false">
view the source</a> of the code above - you can see
that it just creates a table and inserts some data. This table will
be dropped, re-created and populated every time this page is loaded.
</p>
<div id="sqlSource" style="display:none; margin-left: 10px; margin-top: 0">
<pre><sql:transaction dataSource="jdbc/appfuse">
<sql:update>
DROP TABLE IF EXISTS menu_item
</sql:update>
<sql:update>
CREATE TABLE menu_item (
id BIGINT not null,
parent_name VARCHAR(30),
name VARCHAR(30),
title VARCHAR(30),
description VARCHAR(50),
location VARCHAR(255),
target VARCHAR(10),
onclick VARCHAR(100),
onmouseover VARCHAR(100),
onmouseout VARCHAR(100),
image VARCHAR(50),
altImage VARCHAR(30),
tooltip VARCHAR(100),
roles VARCHAR(100),
page VARCHAR(255),
width VARCHAR(5),
height VARCHAR(5),
forward VARCHAR(50),
action VARCHAR(50),
primary key (id)
)
</sql:update>
<sql:update var="updateCount">
INSERT INTO menu_item
(id, name, title)
VALUES
(1,'DatabaseMenu','Database Menu')
</sql:update>
<sql:update var="updateCount">
INSERT INTO menu_item
(id, parent_name, name, title, location)
VALUES
(2,'DatabaseMenu','Yahoo','Yahoo Mail','http://mail.yahoo.com')
</sql:update>
<sql:update var="updateCount">
INSERT INTO menu_item
(id, parent_name, name, title, location)
VALUES
(3,'DatabaseMenu','JavaBlogs','JavaBlogs','http://javablogs.com')
</sql:update>
<sql:update var="updateCount">
INSERT INTO menu_item
(id, name, title, location)
VALUES
(4,'StandaloneMenu','Standalone Menu','http://raibledesigns.com')
</sql:update>
<sql:query var="menus">
SELECT * FROM menu_item order by id;
</sql:query>
</sql:transaction>
<p>
<strong>DONE:</strong> successfully created menu_items table and
added the following entries.
</p>
<display:table name="${menus.rows}" class="list" style="width: 600px">
<display:column property="id"/>
<display:column property="name"/>
<display:column property="parent_name" title="Parent Name"/>
<display:column property="title"/>
<display:column property="location"/>
</display:table></pre>
</div>
<p>
Now let's build a Menu definition with this data. Below is the
Java scriplet code that is used to build this menu.
</p>
<p style="margin-left: 30px; font-style: italic">
In a <b>well-architected</b> application, you might pull the data
from the database using Hibernate, iBATIS, or JDBC. You could then
use a Business Delegate for your who-sees-what logic and call the Delegate
from a ServletFilter, a ServletContextListener or a Login Servlet.
</p>
<%
// I had issues using the existing repository - creating a new one
// seems to solve the problem. If you figure out how to use the default
// Repository and keep your menus from duplicating themselves - please
// let me know!
MenuRepository repository = new MenuRepository();
// Get the repository from the application scope - and copy the
// DisplayerMappings from it.
MenuRepository defaultRepository = (MenuRepository)
application.getAttribute(MenuRepository.MENU_REPOSITORY_KEY);
repository.setDisplayers(defaultRepository.getDisplayers());
Result result = (Result) pageContext.getAttribute("menus");
Map[] rows = result.getRows();
for (int i=0; i < rows.length; i++) {
MenuComponent mc = new MenuComponent();
Map row = rows[i];
String name = (String) row.get("name");
mc.setName(name);
String parent = (String) row.get("parent_name");
System.out.println(name + ", parent is: " + parent);
if (parent != null) {
MenuComponent parentMenu = repository.getMenu(parent);
if (parentMenu == null) {
System.out.println("parentMenu '" + parent + "' doesn't exist!");
// create a temporary parentMenu
parentMenu = new MenuComponent();
parentMenu.setName(parent);
repository.addMenu(parentMenu);
}
mc.setParent(parentMenu);
}
String title = (String) row.get("title");
mc.setTitle(title);
String location = (String) row.get("location");
mc.setLocation(location);
repository.addMenu(mc);
}
pageContext.setAttribute("repository", repository);
%>
<p style="margin-top: 10px; color: red"><code>---- begin scriplet code ----</code></p>
<table style="margin-top: 0; margin-bottom: 10px" cellpadding="3" cellspacing="0" bgcolor="#ffffff">
<tr>
<!-- start source code -->
<td nowrap valign="top" align="left">
<code>
<font color="#3f7f5f">// I had issues using the existing repository - creating a new one</font><br>
<font color="#3f7f5f">// seems to solve the problem. If you figure out how to use the default</font><br>
<font color="#3f7f5f">// Repository and keep your menus from duplicating themselves - please</font><br>
<font color="#3f7f5f">// let me know!</font><br>
<font color="#ffffff"></font><br>
<font color="#000000">MenuRepository repository = </font><font color="#7f0055"><b>new </b></font><font color="#000000">MenuRepository</font><font color="#000000">()</font><font color="#000000">;</font><br>
<font color="#3f7f5f">// Get the repository from the application scope - and copy the</font><br>
<font color="#3f7f5f">// DisplayerMappings from it.</font><br>
<font color="#000000">MenuRepository defaultRepository = </font><font color="#000000">(</font><font color="#000000">MenuRepository</font><font color="#000000">)</font><br>
<font color="#ffffff"> </font><font color="#000000">application.getAttribute</font><font color="#000000">(</font><font color="#000000">MenuRepository.MENU_REPOSITORY_KEY</font><font color="#000000">)</font><font color="#000000">;</font><br>
<font color="#000000">repository.setDisplayers</font><font color="#000000">(</font><font color="#000000">defaultRepository.getDisplayers</font><font color="#000000">())</font><font color="#000000">;</font><br>
<font color="#ffffff"></font><br>
<font color="#000000">Result result = </font><font color="#000000">(</font><font color="#000000">Result</font><font color="#000000">) </font><font color="#000000">pageContext.getAttribute</font><font color="#000000">(</font><font color="#2a00ff">"menus"</font><font color="#000000">)</font><font color="#000000">;</font><br>
<font color="#000000">Map</font><font color="#000000">[] </font><font color="#000000">rows = result.getRows</font><font color="#000000">()</font><font color="#000000">;</font><br>
<font color="#7f0055"><b>for </b></font><font color="#000000">(</font><font color="#7f0055"><b>int </b></font><font color="#000000">i=</font><font color="#990000">0</font><font color="#000000">; i < rows.length; i++</font><font color="#000000">) {</font><br>
<font color="#ffffff"> </font><font color="#000000">MenuComponent mc = </font><font color="#7f0055"><b>new </b></font><font color="#000000">MenuComponent</font><font color="#000000">()</font><font color="#000000">;</font><br>
<font color="#ffffff"> </font><font color="#000000">Map row = rows</font><font color="#000000">[</font><font color="#000000">i</font><font color="#000000">]</font><font color="#000000">;</font><br>
<font color="#ffffff"> </font><font color="#000000">String name = </font><font color="#000000">(</font><font color="#000000">String</font><font color="#000000">) </font><font color="#000000">row.get</font><font color="#000000">(</font><font color="#2a00ff">"name"</font><font color="#000000">)</font><font color="#000000">;</font><br>
<font color="#ffffff"> </font><font color="#000000">mc.setName</font><font color="#000000">(</font><font color="#000000">name</font><font color="#000000">)</font><font color="#000000">;</font><br>
<font color="#ffffff"> </font><font color="#000000">String parent = </font><font color="#000000">(</font><font color="#000000">String</font><font color="#000000">) </font><font color="#000000">row.get</font><font color="#000000">(</font><font color="#2a00ff">"parent_name"</font><font color="#000000">)</font><font color="#000000">;</font><br>
<font color="#ffffff"> </font><font color="#000000">System.out.println</font><font color="#000000">(</font><font color="#000000">name + </font><font color="#2a00ff">", parent is: " </font><font color="#000000">+ parent</font><font color="#000000">)</font><font color="#000000">;</font><br>
<font color="#ffffff"> </font><font color="#7f0055"><b>if </b></font><font color="#000000">(</font><font color="#000000">parent != </font><font color="#7f0055"><b>null</b></font><font color="#000000">) {</font><br>
<font color="#ffffff"> </font><font color="#000000">MenuComponent parentMenu = repository.getMenu</font><font color="#000000">(</font><font color="#000000">parent</font><font color="#000000">)</font><font color="#000000">;</font><br>
<font color="#ffffff"> </font><font color="#7f0055"><b>if </b></font><font color="#000000">(</font><font color="#000000">parentMenu == </font><font color="#7f0055"><b>null</b></font><font color="#000000">) {</font><br>
<font color="#ffffff"> </font><font color="#000000">System.out.println</font><font color="#000000">(</font><font color="#2a00ff">"parentMenu '" </font><font color="#000000">+ parent + </font><font color="#2a00ff">"' doesn't exist!"</font><font color="#000000">)</font><font color="#000000">;</font><br>
<font color="#ffffff"> </font><font color="#3f7f5f">// create a temporary parentMenu</font><br>
<font color="#ffffff"> </font><font color="#000000">parentMenu = </font><font color="#7f0055"><b>new </b></font><font color="#000000">MenuComponent</font><font color="#000000">()</font><font color="#000000">;</font><br>
<font color="#ffffff"> </font><font color="#000000">parentMenu.setName</font><font color="#000000">(</font><font color="#000000">parent</font><font color="#000000">)</font><font color="#000000">;</font><br>
<font color="#ffffff"> </font><font color="#000000">repository.addMenu</font><font color="#000000">(</font><font color="#000000">parentMenu</font><font color="#000000">)</font><font color="#000000">;</font><br>
<font color="#ffffff"> </font><font color="#000000">}</font><br>
<font color="#ffffff"></font><br>
<font color="#ffffff"> </font><font color="#000000">mc.setParent</font><font color="#000000">(</font><font color="#000000">parentMenu</font><font color="#000000">)</font><font color="#000000">;</font><br>
<font color="#ffffff"> </font><font color="#000000">}</font><br>
<font color="#ffffff"> </font><font color="#000000">String title = </font><font color="#000000">(</font><font color="#000000">String</font><font color="#000000">) </font><font color="#000000">row.get</font><font color="#000000">(</font><font color="#2a00ff">"title"</font><font color="#000000">)</font><font color="#000000">;</font><br>
<font color="#ffffff"> </font><font color="#000000">mc.setTitle</font><font color="#000000">(</font><font color="#000000">title</font><font color="#000000">)</font><font color="#000000">;</font><br>
<font color="#ffffff"> </font><font color="#000000">String location = </font><font color="#000000">(</font><font color="#000000">String</font><font color="#000000">) </font><font color="#000000">row.get</font><font color="#000000">(</font><font color="#2a00ff">"location"</font><font color="#000000">)</font><font color="#000000">;</font><br>
<font color="#ffffff"> </font><font color="#000000">mc.setLocation</font><font color="#000000">(</font><font color="#000000">location</font><font color="#000000">)</font><font color="#000000">;</font><br>
<font color="#ffffff"> </font><font color="#000000">repository.addMenu</font><font color="#000000">(</font><font color="#000000">mc</font><font color="#000000">)</font><font color="#000000">;</font><br>
<font color="#000000">}</font><br>
<font color="#000000">pageContext.setAttribute</font><font color="#000000">(</font><font color="#2a00ff">"repository"</font><font color="#000000">, repository</font><font color="#000000">)</font><font color="#000000">;</font></code>
</td>
<!-- end source code -->
</tr>
</table>
<p><code style="color: red">---- end scriplet code ----</code></p>
<p>Now that we've built our menu repository, we can easily display it with the following code:</p>
<p><pre><menu:useMenuDisplayer name="ListMenu" repository="repository">
<menu:displayMenu name="DatabaseMenu"/>
<menu:displayMenu name="StandaloneMenu"/>
</menu:useMenuDisplayer></pre></p>
<p>Which results in:</p>
<div id="menu" style="position: relative; top: 0; left: 0; border: 1px solid silver; width: 175px">
<menu:useMenuDisplayer name="ListMenu" repository="repository">
<menu:displayMenu name="DatabaseMenu"/>
<menu:displayMenu name="StandaloneMenu"/>
</menu:useMenuDisplayer>
</div>
</div>
<div id="footer">
Suggestions or Questions should be addressed to
<a href="mailto:struts-menu-user@lists.sf.net">struts-menu-user@lists.sf.net</a>.
</div>
</body>
</html>