0

Best practice and performance for menu tree | zul vs. java

asked 2008-11-13 19:14:30 +0800

terrytornado gravatar image terrytornado flag of Germany
9393 3 7 16
http://www.oxitec.de/

updated 2008-11-13 21:10:52 +0800

hi all,

i have a technical question about best practice and performance for creating a menu tree.


The menu tree(s) in the planned application should as follows:

1. the menu has follow structure ( + = collapsable ):

  +MainData
     -Tax
     -InvoiceConditions
     -Banks
     - ...

  +OfficeData
     -Customers
     -Orders
     -Employees
     -...

  +Statistical
     -...

  +Setup
     -Usermanagement
     -...

2. is changed by the role of the logged in user
3. should be multi-languagable

---------------------------------------------------------------------------------
A. Is it better to take all existing menu-items in the zul-file
    and by rendering zk looks for each items at the 'if=' attribut?
B. or is it better to build the menu in the underlaying java-file?
----------------------------------------------------------------------------------
C. By my first look it seems that it's a big work to create the tree-menu per coding
    as follows with about 50 items

     // create the menu trees
     Tree menuKategoryFilialstammdaten = new Tree();

     Treechildren treeChildFilialStammdaten = new Treechildren();
     Treeitem treeItem = new Treeitem();
     Treerow treeRow = new Treerow();
     Treecell treeCell = new Treecell();
     treeCell.setLabel("Filialstammdaten");
     treeRow.appendChild(treeCell);
     treeItem.appendChild(treeRow);
     treeChildFilialStammdaten.appendChild(treeItem);
     menuKategoryFilialstammdaten.appendChild(treeChildFilialStammdaten);

     ...

Are there helper classes or is this the false way for creating a tree menu (or using TreeModel? ) ??

Any experience are welcome.
Many thanks for your answers and helps.

Stephan

delete flag offensive retag edit

11 Replies

Sort by » oldest newest

answered 2008-11-13 22:10:53 +0800

meherss gravatar image meherss
27

Stephan,

Actually, I am also trying to do the similar implementation using Menu (your are trying with Tree), you are little head of me :-). When user login, build menu (drill down) depending the organizational unit, roles and group the person belong to. Certainly interested to see how this plays out.

- Meher

link publish delete flag offensive edit

answered 2008-11-14 03:19:14 +0800

johnfatt gravatar image johnfatt
21

Hi,
I am also doing a similar menu from Tree component.
I let my user to maintain(add,update,delete) module, sub-module, and function in my system and then the system will load the Menu from the database. I use SimpleTreeModel and quite a lot HaspMap and TreeMap var(TreeMap is used to maintain the menu item position set by user) to construct all the necessary SimpleTreeNode.
So i think if your system is using "fixed" menu (which mean the menu items are not likely to change), i think using a zul file to render the menu is good enough. Even in production, You can edit the zul directly if needed and no need compiling java etc. Furthermore, zul is more readable, instead of all the messy appendChild or setParent thingy in a java file.

Regards,
John

link publish delete flag offensive edit

answered 2008-11-14 09:18:46 +0800

ziccardi gravatar image ziccardi
321 7

updated 2008-11-14 09:19:15 +0800

Here is my opinion.

The real answer depends on your point of view.

If your issue is performance, than a JAVA file would be surely better (zscripts are interpreted by beanshell).
Using Java, I would use ZUL to describe the view, a TreeModel for the data, a renderer to create the tree row and a composer to interact with the zul components.

If your issue is 'how much readable is your code', than ZUL is should be your choice.

About the point C:
If you want to build the tree by java code, don't build it manually. Use a model and e renderer: it will save you lot of code.

Conclusions
Usually I prefer to use the first solution: ZUL + MODEL + RENDERER + COMPOSER.
It makes the code quite readable and easy to write, making the development of the application very fast.

Regards,
Massimiliano

link publish delete flag offensive edit

answered 2008-12-18 08:52:25 +0800

robinbak gravatar image robinbak
42

Hello,

My personal favorite for this problem is the following:
- create your own model, for example a simple arraylist with a Java Bean's with properties like: "label", "action", "roles" etc.
- use XStream to write this model to an xml file.
- Make a helper class to generate (dynamically) a tree menu based on the model from above.

The advantages:
- Creating a new menu is now just a matter of copy/paste/edit a new (Xstream) menu xml file.
- And this menu can be role based

gr

link publish delete flag offensive edit

answered 2008-12-18 12:01:50 +0800

terrytornado gravatar image terrytornado flag of Germany
9393 3 7 16
http://www.oxitec.de/

@all

many thanks for your proposals. Yes, it's what i thinking too.

I would prefer the following way:
- a empty zul-file (only the window tag)
- a model for the menu entries for filtering the user,role,menu, action
- a renderer
- a controller

At time we have on our Zkoss sample app (Zkoss+Spring+Hibernate) finish the migrating from 'fat' zul-file menu to manually java-generated menu. But the next step should to do it with a model and a renderer because the role,user,menu,action,destination should be come from the database.

I think so it's the best way to reuse this menu logic in other apps or extend by needing.

1. Does somebody have samples for a model and renderer for using as menu logic in
a menu first with two depth (Menu-Category + Menu-Entry ?
2. Our sample app should go for everyone. So we need an address to host the eclipse project.


thanks Stephan
sge(at)forsthaus(dot)de

link publish delete flag offensive edit

answered 2008-12-19 09:41:36 +0800

robinbak gravatar image robinbak
42

Hello here is an example how to dynamically create a tree menu based on a dedicated model:

###--- TestMenu.zul -------

<?page id="testZul" title=" New ZUL Title" cacheable="false"
language="xul/html" zscriptLanguage="Java" contentType="text/html;charset=UTF-8"?>

<zk>
<window id="w" use="com.dll.zk.comp.RoleBasedTreeMenuCtrl">
<tree id="idTree" visible="true" height="100px" width="300px"/>
</window>
</zk>

###--- menu.xml
<list>
<com.dll.zk.comp.RoleBasedMenuItem>
<label>Registreer</label>
<description>Registreer de inkomende documenten</description>
<action>REGISTER_INCOMING_DOC</action>
</com.dll.zk.comp.RoleBasedMenuItem>

<com.dll.zk.comp.RoleBasedMenuItem>
<label>Call Me</label>
<description>Bel die klant nu eens op</description>
<roles>
<string>MO_AGENT</string>
</roles>
<action>CUSTOMER_CALLME</action>
<arguments>
<string>P1</string>
</arguments>
</com.dll.zk.comp.RoleBasedMenuItem>
</list>

###--- com/dll/zk/comp/RoleBasedMenuItem.java

package com.dll.zk.comp;

import java.util.ArrayList;
import java.util.List;

public class RoleBasedMenuItem {

private String label;
private String description;
private List<String> roles;
private String action;
private List<String> arguments;
private List<RoleBasedMenuItem> children;

//---- getters & setters omiited
}


###--- com/dll/zk/comp/RoleBasedTreeMenuCtrl.java

package com.dll.zk.comp;

import java.util.List;

import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zul.Tree;
import org.zkoss.zul.Treecell;
import org.zkoss.zul.Treechildren;
import org.zkoss.zul.Treeitem;
import org.zkoss.zul.Treerow;
import org.zkoss.zul.Window;

import com.dll.cf.crm.constants.EventsConst;
import com.dll.cf.crm.ctrl.CommandCtrl;
import com.dll.cxp.frw.util.XstreamHelper;
import com.dll.zk.helper.EventBroker;

public class RoleBasedTreeMenuCtrl extends Window {

private static final long serialVersionUID = -1636111928349775166L;

private RoleBasedMenuItem menuItems;

public static final String ATTR_MODEL_ITEM = "modelItem";

public void onCreate()
{
testMe();
}

private void testMe() {
CommandCtrl.getInstance();
String roles[] = new String[] {"MO_AGENT"};
generateMenu("/menu.xml", roles);
}

@SuppressWarnings("unchecked")
public void generateMenu(String aResourceName, String aRoles[])
{
List<RoleBasedMenuItem> menuitems = (List<RoleBasedMenuItem>)
XstreamHelper.readObject(this, aResourceName);

generateMenu(menuitems, aRoles);
}

public void generateMenu(List<RoleBasedMenuItem> aMenuItems, String aRoles[])
{
Treechildren treeChildren = getTree().getTreechildren();
if (treeChildren == null) {
treeChildren = new Treechildren();
getTree().appendChild(treeChildren);
}

for (RoleBasedMenuItem rbMenuItem : aMenuItems) {

if (isInRole(rbMenuItem, aRoles)) {
Treerow treerow = new Treerow();

Treecell treecell = makeTreeCell(rbMenuItem);
treerow.appendChild(treecell);

Treeitem treeitem = new Treeitem();
treeitem.appendChild(treerow);
treeChildren.appendChild(treeitem);
}
}
}

@SuppressWarnings("unchecked")
private Treecell makeTreeCell(RoleBasedMenuItem rbMenuItem)
{
Treecell result = new Treecell(rbMenuItem.getLabel());
result.addEventListener("onClick", new HandleMenuClick());
result.getAttributes().put(ATTR_MODEL_ITEM, rbMenuItem);
result.setTooltip(rbMenuItem.getDescription());
return result;
}

/**
* if no menuItem has no roles, then we assume it is for the whole world.
* the same is true if the user has NULL roles.
* @param aRbMenuItem
* @param allowedRoles
* @return
*/
private boolean isInRole(RoleBasedMenuItem aRbMenuItem, String allowedRoles[])
{
if (aRbMenuItem.getRoles() == null || aRbMenuItem.getRoles().size() == 0) {
return true;
} else {
if (allowedRoles == null) {
return true;
} else {
for (String allowedRole : allowedRoles) {
for (String role : aRbMenuItem.getRoles()) {
if (role != null && allowedRole != null &&
allowedRole.toUpperCase().equals(role.toUpperCase())) {

return true;
}
}
}
}
}
return false;
}

public Tree getTree()
{
return (Tree) getFellow("idTree");
}

public RoleBasedMenuItem getMenuItems() {
return menuItems;
}

public void setMenuItems(RoleBasedMenuItem menuItems) {
this.menuItems = menuItems;
}

//--- inner class
private class HandleMenuClick implements EventListener
{
public void onEvent(Event event) throws Exception
{
Treecell tc = (Treecell) event.getTarget();
RoleBasedMenuItem rbItem = (RoleBasedMenuItem) tc.getAttribute(ATTR_MODEL_ITEM);
EventBroker.getInstance().dispatchEvent(EventsConst.MENU_CLICK, rbItem);
}
}
}

###----- com/dll/zk/helper/EventBroker

package com.dll.zk.helper;

import org.zkoss.zk.ui.AbstractComponent;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.Events;

public class EventBroker extends AbstractComponent {

private static final long serialVersionUID = 1840221861723658854L;

private static final String KEY = "EventBrokerKey";

public static EventBroker getInstance()
{
EventBroker result = (EventBroker) Executions.getCurrent().getDesktop().getSession().getAttribute(KEY);
if (result == null) {
result = new EventBroker();
Executions.getCurrent().getDesktop().getSession().setAttribute(KEY, result);
}
return result;
}

public void dispatchEvent(String aEventname, Object aData)
{
Events.sendEvent(new Event(aEventname, this, aData));
}
}

link publish delete flag offensive edit

answered 2008-12-19 10:43:10 +0800

terrytornado gravatar image terrytornado flag of Germany
9393 3 7 16
http://www.oxitec.de/

@robinbak

Many thanks. It looks interesting. I need a little bit time to check how it's work and do an adaption from the menu.xml to database tables

Stephan

link publish delete flag offensive edit

answered 2012-06-06 01:06:50 +0800

slave gravatar image slave
24

Hi robinbak,


Am facing some problem with the code you post.

Where to find these 3 classes ?

import com.dll.cf.crm.constants.EventsConst;
import com.dll.cf.crm.ctrl.CommandCtrl;
import com.dll.cxp.frw.util.XstreamHelper;

Tks in advance.

Regards

/Slave

link publish delete flag offensive edit

answered 2012-06-07 08:51:54 +0800

hebrew gravatar image hebrew
3

Hi Robinbak,

Please I need
import com.dll.cf.crm.constants.EventsConst;
import com.dll.cf.crm.ctrl.CommandCtrl;
import com.dll.cxp.frw.util.XstreamHelper;

to practice this example? can i download sample code from you robinbak?

link publish delete flag offensive edit

answered 2012-06-08 02:44:08 +0800

marcelodecampos gravatar image marcelodecampos
183

updated 2012-06-08 02:48:27 +0800

Fellows,

Just an example!!!!!
My project is using JBOSS 7 + zk (of course) + EJB3.1+PostgreSQL


This is my menu entity. As shown, this entity is bound in task entity.

@Entity
@Table( name = "menu" )
@NamedQueries( { @NamedQuery( name = Menu.getTopMenu, query = "select o from Menu o where o.parent is null" ) } )
public class Menu implements Serializable, TaskRelationInterface, SelfRelationInterface<Menu>, Comparable<Menu>
{
	private static final long serialVersionUID = 1L;

	public static final String getTopMenu = "Menu.getTopMenu";

	@Id
	@Column( name = "mnu_id_in", unique = true, nullable = false )
	private Integer id;

	@ManyToOne
	@JoinColumn( name = "med_id_in" )
	private Media media;

	@Column( name = "mnu_autocheck_bt" )
	private Boolean autocheck;

	@Column( name = "mnu_checked_bt" )
	private Boolean checked;

	@Column( name = "mnu_checkmark_bt" )
	private Boolean checkmark;

	@Column( name = "mnu_description_ch", nullable = false, length = 64 )
	private String description;

	@Column( name = "mnu_disabled_bt" )
	private Boolean disabled;

	@Column( name = "mnu_separator_before_bt" )
	private Boolean separatorBefore;

	@Column( name = "mnu_sequence_in", nullable = false )
	private Integer sequence;

	@Column( name = "mnu_url_ch", length = 1024 )
	private String url;

	@Column( name = "mnu_image_path_ch", length = 256, nullable = true )
	private String imagePath;

	// bi-directional many-to-one association to Menu
	@ManyToOne( fetch = FetchType.EAGER )
	@JoinColumn( name = "mnu_parent_id" )
	private Menu parent;

	// bi-directional many-to-one association to Menu
	@OneToMany( mappedBy = "parent", fetch = FetchType.EAGER )
	private List<Menu> childs;

	@ManyToMany( mappedBy = "menus" )
	List<Task> tasks;

	public Menu( )
	{
	}

	public Integer getId( )
	{
		return this.id;
	}

	public void setId( Integer mnuIdIn )
	{
		this.id = mnuIdIn;
	}

	public Boolean getAutocheck( )
	{
		return this.autocheck;
	}

	public void setAutocheck( Boolean mnuAutocheckBt )
	{
		this.autocheck = mnuAutocheckBt;
	}

	public Boolean getChecked( )
	{
		return this.checked;
	}

	public void setChecked( Boolean mnuCheckedBt )
	{
		this.checked = mnuCheckedBt;
	}

	public Boolean getCheckmark( )
	{
		return this.checkmark;
	}

	public void setCheckmark( Boolean mnuCheckmarkBt )
	{
		this.checkmark = mnuCheckmarkBt;
	}

	public String getDescription( )
	{
		return this.description;
	}

	public void setDescription( String mnuDescriptionCh )
	{
		this.description = mnuDescriptionCh;
	}

	public Boolean getDisabled( )
	{
		return this.disabled;
	}

	public void setDisabled( Boolean mnuDisabledBt )
	{
		this.disabled = mnuDisabledBt;
	}

	public Boolean getSeparatorBefore( )
	{
		return this.separatorBefore;
	}

	public void setSeparatorBefore( Boolean mnuSeparatorBeforeBt )
	{
		this.separatorBefore = mnuSeparatorBeforeBt;
	}

	public Integer getSequence( )
	{
		return this.sequence;
	}

	public void setSequence( Integer mnuSequenceIn )
	{
		this.sequence = mnuSequenceIn;
	}

	public String getUrl( )
	{
		return this.url;
	}

	public void setUrl( String mnuUrlCh )
	{
		this.url = mnuUrlCh;
	}

	public Menu getParent( )
	{
		return this.parent;
	}

	@Override
	public void setParent( Menu parent )
	{
		if ( parent != null ) {
			if ( parent.equals( getParent( ) ) == false ) {
				if ( parent.getParent( ) != null ) {
					parent.getParent( ).remove( this );
				}
				this.parent = parent;
				getParent( ).add( this );
			}
		}
		else {
			this.parent = null;
		}
	}

	public List<Menu> getChilds( )
	{
		if ( this.childs == null ) {
			this.childs = new ArrayList<Menu>( );
		}
		return this.childs;
	}

	public void setChilds( List<Menu> menus )
	{
		this.childs = menus;
	}

	public void setMedia( Media media )
	{
		this.media = media;
	}

	public Media getMedia( )
	{
		return this.media;
	}

	@Override
	public List<Task> getTasks( )
	{
		return this.tasks;
	}

	@Override
	public void setTasks( List<Task> tasks )
	{
		this.tasks = tasks;
	}

	@Override
	public int compareTo( Menu o )
	{
		if ( o == null ) {
			return 1;
		}
		return getId( ).compareTo( o.getId( ) );
	}

	@Override
	public boolean equals( Object obj )
	{
		if ( obj instanceof Menu ) {
			return getId( ).equals( ( (Menu) obj ).getId( ) );
		}
		else if ( obj instanceof Integer ) {
			return getId( ).equals( obj );
		}
		else {
			return false;
		}
	}

	@Override
	public Task add( Task task )
	{
		if ( task != null && getTasks( ).contains( task ) == false ) {
			getTasks( ).add( task );
			task.add( this );
		}
		return task;
	}

	@Override
	public Task remove( Task task )
	{
		if ( task != null )
		{
			int nIndex = getTasks( ).indexOf( task );
			if ( nIndex >= 0 ) {
				getTasks( ).remove( nIndex );
				task.remove( this );
			}
		}
		return task;

	}

	@Override
	public void add( Menu child )
	{
		if ( child != null && getChilds( ).contains( child ) == false ) {
			assert ( getChilds( ).add( child ) );
			child.setParent( this );
		}
	}

	@Override
	public Menu remove( Menu child )
	{
		if ( child != null && getChilds( ).contains( child ) ) {
			assert ( getChilds( ).remove( child ) );
			child.setParent( null );
		}
		return child;
	}

	@Override
	public String toString( )
	{
		return getId( ).toString( ) + " - " + getDescription( );
	}

	public String getImagePath( )
	{
		return this.imagePath;
	}

	public void setImagePath( String imagePath )
	{
		this.imagePath = imagePath;
	}

}

This is my task Entity. This entity is kind of middle between a menu item and a role.

@Entity
@Table( name = "task" )
@NamedQueries( { @NamedQuery( name = Task.getTop, query = "select o from Task o where o.parent is null" ) } )
public class Task implements Serializable, Comparable<Task>
{
	private static final long serialVersionUID = 1L;
	public static final String getTop = "Task.getTopTasks";

	@Id
	@Column( name = "tsk_id_in", unique = true, nullable = false )
	private Integer id;

	@Column( name = "tsk_description_ch", nullable = false, length = 64 )
	private String description;

	// bi-directional many-to-one association to Role
	@ManyToOne( fetch = FetchType.EAGER )
	@JoinColumn( name = "tsk_parent_id" )
	private Task parent;

	// bi-directional many-to-one association to Role
	@OneToMany( mappedBy = "parent", fetch = FetchType.EAGER )
	private List<Task> childs;

	@ManyToMany
	@JoinTable( name = "task_menu", joinColumns = { @JoinColumn( name = "tsk_id_in", nullable = false ) },
			inverseJoinColumns = { @JoinColumn( name = "mnu_id_in", nullable = false ) } )
	private List<Menu> menus;

	@ManyToMany( mappedBy = "tasks" )
	List<Role> roles;

	public Task( )
	{
	}

	public Integer getId( )
	{
		return this.id;
	}

	public void setId( Integer tskIdIn )
	{
		this.id = tskIdIn;
	}

	public String getDescription( )
	{
		return this.description;
	}

	public void setDescription( String tskDescriptionCh )
	{
		this.description = tskDescriptionCh;
	}

	public List<Menu> getMenus( )
	{
		if ( this.menus == null ) {
			this.menus = new ArrayList<Menu>( );
		}
		return this.menus;
	}

	public void setMenus( List<Menu> menus )
	{
		this.menus = menus;
	}

	public Task getParent( )
	{
		return this.parent;
	}

	public void setParent( Task parent )
	{
		this.parent = parent;
	}

	public List<Task> getChilds( )
	{
		if ( this.childs == null ) {
			this.childs = new ArrayList<Task>( );
		}
		return this.childs;
	}

	public void setChilds( List<Task> childs )
	{
		this.childs = childs;
	}

	public Role remove( Role role )
	{
		if ( role != null ) {
			int nIndex = getRoles( ).indexOf( role );
			if ( nIndex >= 0 ) {
				getRoles( ).remove( nIndex );
				role.remove( this );
			}
		}
		return role;
	}

	public Role add( Role role )
	{
		if ( role != null && getRoles( ).contains( role ) == false ) {
			getRoles( ).add( role );
		}
		return role;
	}

	public List<Role> getRoles( )
	{
		if ( this.roles == null ) {
			this.roles = new ArrayList<Role>( );
		}
		return this.roles;
	}

	public void setRoles( List<Role> roles )
	{
		this.roles = roles;
	}

	public Menu remove( Menu menu )
	{
		if ( menu != null ) {
			int nIndex = getMenus( ).indexOf( menu );
			if ( nIndex >= 0 )
			{
				getMenus( ).remove( nIndex );
				menu.remove( this );
			}
		}
		return menu;
	}

	public Menu add( Menu menu )
	{
		if ( menu != null && getMenus( ).contains( menu ) == false ) {
			getMenus( ).add( menu );
		}
		return menu;
	}

	@Override
	public int compareTo( Task o )
	{
		return getId( ).compareTo( o.getId( ) );
	}

	@Override
	public boolean equals( Object obj )
	{
		if ( obj instanceof Task ) {
			return getId( ).equals( ( (Task) obj ).getId( ) );
		}
		else {
			return false;
		}
	}

	@Override
	public String toString( )
	{
		String str = getId( ).toString( ) + " - " + getDescription( );
		if ( getParent( ) != null ) {
			str += " Parent: " + getParent( ).toString( );
		}
		return str;
	}

	public Task remove( Task child )
	{
		if ( child != null )
		{
			int nIndex = getChilds( ).indexOf( child );
			if ( nIndex >= 0 ) {
				getChilds( ).remove( nIndex );
				child.setParent( null );
			}
		}
		return child;
	}

	public Task add( Task child )
	{
		if ( child != null && getChilds( ).contains( child ) == false ) {
			getChilds( ).add( child );
			child.setParent( this );
		}
		return child;
	}
}


Role entity. Every system user is bound by a role. So it´s easy to see Role->Task->Menu!!!

@Entity
@Table( name = "role" )
public class Role implements Serializable, TaskRelationInterface, SelfRelationInterface<Role>
{
	private static final long serialVersionUID = 1L;

	@Id
	@Column( name = "rol_id_in", unique = true, nullable = false )
	private Integer id;

	@Column( name = "rol_default_bt" )
	private Boolean rolDefaultBt;

	@Column( name = "rol_description_ch", nullable = false, length = 64 )
	private String description;

	// bi-directional many-to-one association to Role
	@ManyToOne( fetch = FetchType.EAGER )
	@JoinColumn( name = "rol_parent_id" )
	private Role parent;

	// bi-directional many-to-one association to Role
	@OneToMany( mappedBy = "parent", fetch = FetchType.EAGER )
	private List<Role> childs;

	@ManyToMany
	@JoinTable( name = "permission_assignment", joinColumns = { @JoinColumn( name = "rol_id_in", nullable = false ) },
			inverseJoinColumns = { @JoinColumn( name = "tsk_id_in", nullable = false ) } )
	private List<Task> tasks;

	public Role( )
	{
	}

	public Role( Integer id, String description )
	{
		setId( id );
		setDescription( description );
	}

	public Integer getId( )
	{
		return this.id;
	}

	public void setId( Integer rolIdIn )
	{
		this.id = rolIdIn;
	}

	public Boolean getRolDefaultBt( )
	{
		return this.rolDefaultBt;
	}

	public void setRolDefaultBt( Boolean rolDefaultBt )
	{
		this.rolDefaultBt = rolDefaultBt;
	}

	public String getDescription( )
	{
		return this.description;
	}

	public void setDescription( String rolDescriptionCh )
	{
		this.description = rolDescriptionCh;
	}

	public Role getParent( )
	{
		return this.parent;
	}

	/*
	 * A entidade corrente está recebendo a role como parent
	 */
	@Override
	public void setParent( Role parent )
	{
		if ( parent != null ) {
			if ( parent.equals( getParent( ) ) == false ) {
				if ( parent.getParent( ) != null ) {
					parent.getParent( ).remove( this );
				}
				this.parent = parent;
				getParent( ).add( this );
			}
		}
		else {
			this.parent = null;
		}
	}

	@Override
	public void add( Role child )
	{
		if ( child != null && getChilds( ).contains( child ) == false ) {
			getChilds( ).add( child );
			child.setParent( this );
		}
	}

	@Override
	public Role remove( Role child )
	{
		if ( child != null )
		{
			if ( getChilds( ).contains( child ) ) {
				getChilds( ).remove( child );
				child.setParent( null );
			}
		}
		return child;
	}

	public List<Role> getChilds( )
	{
		if ( this.childs == null ) {
			this.childs = new ArrayList<Role>( );
		}
		return this.childs;
	}

	public void setChilds( List<Role> roles )
	{
		this.childs = roles;
	}

	@Override
	public List<Task> getTasks( )
	{
		if ( this.tasks == null ) {
			this.tasks = new ArrayList<Task>( );
		}
		return this.tasks;
	}

	@Override
	public void setTasks( List<Task> tasks )
	{
		this.tasks = tasks;
	}

	@Override
	public Task add( Task task )
	{
		if ( task != null && getTasks( ).contains( task ) == false ) {
			getTasks( ).add( task );
			task.add( this );
		}
		return task;
	}

	@Override
	public Task remove( Task task )
	{
		if ( task != null ) {
			int nIndex = getTasks( ).indexOf( task );
			if ( nIndex >= 0 ) {
				getTasks( ).remove( nIndex );
				task.remove( this );
			}
		}
		return task;
	}
}


This is my menu facade. It´s a simple SessionBean

@Stateless( name = "MenuFacade", mappedName = "MenuFacade" )
@LocalBean
public class MenuFacadeBean extends SimpleSessionBean<Menu> implements MenuFacade, MenuFacadeLocal
{
	@EJB
	private CollaboratorSessionLocal collaboratorSession;

	@EJB
	TaskSessionLocal taskSession;

	@Override
	protected Class<Menu> getEntityClass( )
	{
		return Menu.class;
	}

/*This is where some system user get his/her menus*/
<b >
	@Override
	public List<Menu> getMenus( Collaborator collaborator ) throws ApplicationException
	{
		List<Menu> availableMenus = Collections.emptyList( );
		if ( collaborator == null ) {
			return Collections.emptyList( );
		}
		collaborator = this.collaboratorSession.merge( collaborator );
		availableMenus = new ArrayList<Menu>( );
		for ( Role role : collaborator.getRoles( ) )
		{
			addRoleToMenu( role, availableMenus );
		}
		return availableMenus;
	}
	@Override
	public void addRoleToMenu( Role role, List<Menu> availableMenus )
	{
		if ( role != null ) {
			for ( Role sub : role.getChilds( ) ) {
				addRoleToMenu( sub, availableMenus );
			}
			for ( Task task : role.getTasks( ) ) {
				addTaskToMenu( task, availableMenus );
			}
		}
	}
	private void addTaskToMenu( Task task, List<Menu> availableMenus )
	{
		if ( task != null ) {
			for ( Task child : task.getChilds( ) ) {
				addTaskToMenu( child, availableMenus );
			}
			for ( Menu menu : task.getMenus( ) ) {
				addMenus( menu, availableMenus );
			}
		}
	}

	private void addMenus( Menu item, List<Menu> availableMenus )
	{
		if ( item != null ) {
			addMenus( item.getParent( ), availableMenus );
			if ( availableMenus.contains( item ) == false ) {
				availableMenus.add( item );
			}
		}
	}
</b>

	@Override
	public List<Menu> getTopContextMenu( ) throws ApplicationException
	{
		return findByNamedQuery( Menu.getTopMenu );
	}

	@Override
	public List<Task> getRootTask( )
	{
		return getTaskSession( ).getRootTasks( );
	}

	protected CollaboratorSessionLocal getCollaboratorSession( )
	{
		return this.collaboratorSession;
	}

	protected TaskSessionLocal getTaskSession( )
	{
		return this.taskSession;
	}

	@Override
	public List<Task> getTaks( Menu entity )
	{
		List<Task> tasks = new ArrayList<Task>( );
		Menu merged = get( entity.getId( ) );
		addTask( tasks, merged );
		return tasks;
	}

	private void addTask( List<Task> tasks, Menu entity )
	{
		for ( Menu item : entity.getChilds( ) )
		{
			addTask( tasks, item );
		}
		for ( Task task : entity.getTasks( ) )
		{
			if ( tasks.contains( task ) == false ) {
				tasks.add( task );
			}
		}
	}

	@Override
	public void changeParent( Menu entity, Menu newParent )
	{
		Menu targetEntity = get( entity.getId( ) );
		Menu targetParent = get( newParent.getId( ) );

		Menu oldParent = targetEntity.getParent( );
		if ( oldParent != null ) {
			targetEntity = oldParent.remove( targetEntity );
		}
		targetParent.add( targetEntity );
	}

	@Override
	public Menu add( Menu item, List<Task> tasks )
	{
		for ( Task task : tasks ) {
			add( item, task );
		}
		return item;
	}

	@Override
	public Menu add( Menu item, Task task )
	{
		Menu merged = get( item.getId( ) );
		if ( merged != null ) {
			Task taskMerged = this.taskSession.get( task.getId( ) );
			merged.add( taskMerged );
		}
		return merged;
	}

	@Override
	public Menu remove( Menu item, Task task )
	{
		Menu merged = get( item.getId( ) );
		if ( merged != null ) {
			Task taskMerged = this.taskSession.get( task.getId( ) );
			merged.remove( taskMerged );
		}
		return merged;
	}
}

This is the controller that really use that SessionBean

public class IndexController extends BaseLoggedMDIController implements IClickEvent
{
	private static final long serialVersionUID = 4709477127092943298L;
	public static final String queueName = "changeCompanyEventQueue";

	private transient MenuFacade session = null;

	@Wire( "#mainMenu" )
	private Menubar mainMenu;

	private DynamicMenu dynamicMenu = null;

	@Override
	public void onClick( MouseEvent evt )
	{
		Menuitem menuItem;

		if ( evt.getTarget( ) instanceof Menuitem ) {
			menuItem = (Menuitem) evt.getTarget( );
			Object obj;

			obj = menuItem.getAttribute( MenuController.attrMenu );
			if ( obj != null ) {
				/*
				 * TODO: add menu routine
				 */
			}
			else {
				if ( SysUtils.isEmpty( menuItem.getValue( ) ) == false ) {
					loadPage( menuItem.getValue( ), true );
				}
			}
		}
	}

	public void onNotify( CompanyEventChange evt ) throws ApplicationException
	{
		if ( getMdiApplication( ) != null && getMdiApplication( ).getChildren( ) != null ) {
			clear( );
		}
		if ( this.mainMenu == null ) {
			return;
		}
		if ( this.mainMenu.getChildren( ) != null ) {
			this.mainMenu.getChildren( ).clear( );
		}
<b >
		List<br.com.mcampos.ejb.security.menu.Menu> menus = getSession( ).getMenus( getCurrentCollaborator( ) );
		for ( br.com.mcampos.ejb.security.menu.Menu item : menus ) {
			getDynamicMenu( ).getParentComponent( item );
		}
</b>
	}

	private void clear( )
	{
		try {
			/*
			 * initPage = getUserLocator().getInitialPage( getLoggedInUser() );
			 * if ( !SysUtils.isEmpty( initPage ) ) { gotoPage( initPage,
			 * mdiApplication ); } else
			 */
			getMdiApplication( ).getChildren( ).clear( );
		}
		catch ( Exception e ) {
			showErrorMessage( e.getMessage( ), "Erro" );
		}
	}

	private MenuFacade getSession( )
	{
		try {
			if ( this.session == null ) {
				this.session = (MenuFacade) ServiceLocator.getInstance( ).getRemoteSession( MenuFacade.class );
			}
		}
		catch ( NamingException e ) {
			e.printStackTrace( );
		}
		return this.session;
	}

	@Override
	public void doAfterCompose( Window comp ) throws Exception
	{
		super.doAfterCompose( comp );
		EventQueues.lookup( queueName ).subscribe( new EventListener<Event>( )
		{
			@Override
			public void onEvent( Event evt )
			{
				if ( evt instanceof CompanyEventChange ) {
					try {
						onNotify( (CompanyEventChange) evt );
					}
					catch ( ApplicationException e ) {
						e.printStackTrace( );
						e = null;
					}
				}
			}
		} );
	}

	protected DynamicMenu getDynamicMenu( )
	{
		if ( this.dynamicMenu == null ) {
			this.dynamicMenu = new DynamicMenu( this.mainMenu, this );
		}
		return this.dynamicMenu;
	}

}


This is the real deal! How to make (mount) the menu!!!

public final class DynamicMenu implements Serializable
{
	/**
	 * 
	 */
	private static final long serialVersionUID = -6069397668281548120L;

	protected static final String attrMenu = "dto";

	private IClickEvent clickEvent;

	private Menubar mainMenu;

	public DynamicMenu( Menubar mainMenu, IClickEvent evt )
	{
		setMainMenu( mainMenu );
		setClickEvent( evt );
	}

	public Component createMenu( br.com.mcampos.ejb.security.menu.Menu item )
	{
		Component ret;

		if ( SysUtils.isEmpty( item.getUrl( ) ) == false ) {
			Menuitem menuItem;

			menuItem = new Menuitem( item.getDescription( ) );
			menuItem.setAutocheck( item.getAutocheck( ) );
			menuItem.setChecked( item.getChecked( ) );
			menuItem.setCheckmark( item.getCheckmark( ) );
			menuItem.setDisabled( item.getDisabled( ) );
			menuItem.setAttribute( attrMenu, item.getUrl( ) );
			menuItem.setValue( item.getUrl( ) );
			menuItem.setImage( item.getImagePath( ) );
			createListener( menuItem );
			ret = menuItem;
		}
		else {
			Menu menuitem = new Menu( item.getDescription( ) );
			menuitem.appendChild( new Menupopup( ) );
			ret = menuitem;
		}
		ret.setAttribute( "index", item.getSequence( ) );
		ret.setAttribute( "id", item.getId( ) );
		return ret;

	}

	private void createListener( Menuitem item )
	{
		if ( getClickEvent( ) != null )
		{
			item.addEventListener( Events.ON_CLICK, new EventListener<Event>( )
			{
				@Override
				public void onEvent( Event event ) throws Exception
				{
					getClickEvent( ).onClick( (MouseEvent) event );
				}
			} );
		}
	}

	protected IClickEvent getClickEvent( )
	{
		return this.clickEvent;
	}

	protected void setClickEvent( IClickEvent clickEvent )
	{
		this.clickEvent = clickEvent;
	}

	protected Menubar getMainMenu( )
	{
		return this.mainMenu;
	}

	protected void setMainMenu( Menubar mainMenu )
	{
		this.mainMenu = mainMenu;
	}

	public Component getParentComponent( br.com.mcampos.ejb.security.menu.Menu item )
	{
		Component base = null;
		Component target = null;

		/*
		 * Recursivamente vai ate o menu pai
		 */
		if ( item.getParent( ) != null ) {
			base = getParentComponent( item.getParent( ) );
			if ( base instanceof Menu ) {
				base = base.getChildren( ).get( 0 );
			}
		}
		if ( base == null ) {
			base = getMainMenu( );
		}

		/*
		 * Procura por um menu ja criado com o mesmo id do item
		 */
		for ( Component c : base.getChildren( ) )
		{
			Integer id = (Integer) c.getAttribute( "id" );
			if ( id != null && id.equals( item.getId( ) ) ) {
				target = c;
				break;
			}
		}

		/*
		 * Nao achou entao cria no lugar correto
		 */
		if ( target == null )
		{
			for ( int nIndex = 0; nIndex < base.getChildren( ).size( ); nIndex++ )
			{
				Component current = base.getChildren( ).get( nIndex );
				Integer sequence = (Integer) current.getAttribute( "index" );
				if ( sequence != null && sequence.compareTo( item.getSequence( ) ) > 0 ) {
					target = createMenu( item );
					base.getChildren( ).add( nIndex, target );
					break;
				}
			}
			if ( target == null ) {
				target = createMenu( item );
				if ( ( base instanceof Menubar && target instanceof Menu )
						|| ( base instanceof Menupopup && ( target instanceof Menu || target instanceof Menuitem ) ) ) {
					base.getChildren( ).add( target );
				}
			}
		}
		return target;
	}
}


Some SQL (Postgres) for fun - I really didin't test to see if its working, becouse this is a part of my projetct - as same as above!


DROP TABLE IF EXISTS public.permission_assignment CASCADE ;



DROP TABLE IF EXISTS public.role CASCADE ;



DROP TABLE IF EXISTS public.task_menu CASCADE ;



DROP TABLE IF EXISTS public.task CASCADE ;



DROP TABLE IF EXISTS public.menu CASCADE ;



CREATE TABLE public.menu
(
	mnu_id_in            INTEGER NOT NULL ,
	mnu_parent_id        INTEGER NULL ,
	mnu_description_ch   VARCHAR(64) NOT NULL ,
	mnu_url_ch           VARCHAR(1024) NULL ,
	mnu_sequence_in      INTEGER NOT NULL ,
	med_id_in            INTEGER NULL ,
	mnu_separator_before_bt BOOLEAN DEFAULT  FALSE  NULL ,
	mnu_autocheck_bt     BOOLEAN DEFAULT  FALSE  NULL ,
	mnu_checked_bt       BOOLEAN DEFAULT  FALSE  NULL ,
	mnu_checkmark_bt     BOOLEAN DEFAULT  FALSE  NULL ,
	mnu_disabled_bt      BOOLEAN DEFAULT  FALSE  NULL ,
	mnu_image_path_ch    VARCHAR(256) NULL ,
CONSTRAINT  PK_Menu PRIMARY KEY (mnu_id_in) USING INDEX TABLESPACE TB_CLOUD_INDEX ,CONSTRAINT  UK_MenuSequence UNIQUE (mnu_parent_id,mnu_sequence_in) USING INDEX TABLESPACE TB_CLOUD_INDEX ,
CONSTRAINT FK_Menu_ParentMenu FOREIGN KEY (mnu_parent_id) REFERENCES public.menu (mnu_id_in) ON DELETE CASCADE,
CONSTRAINT FK_Menu_Image FOREIGN KEY (med_id_in) REFERENCES public.media (med_id_in) ON DELETE SET NULL
)
	TABLESPACE tb_cloud_data;
CREATE UNIQUE INDEX public.UK_MenuSequence ON public.menu
(mnu_parent_id   ASC,mnu_sequence_in   ASC)
	TABLESPACE tb_cloud_index;

DROP TRIGGER IF EXISTS trgDeleteMediaFrommenu ON menu;
CREATE TRIGGER trgDeleteMediaFrommenu after delete ON menu FOR EACH ROW EXECUTE PROCEDURE deleteMedia();

CREATE TABLE public.task
(
	tsk_id_in            INTEGER NOT NULL ,
	tsk_parent_id        INTEGER NULL ,
	tsk_description_ch   VARCHAR(64) NOT NULL ,
CONSTRAINT  XPKTasks PRIMARY KEY (tsk_id_in) USING INDEX TABLESPACE TB_CLOUD_INDEX ,
CONSTRAINT Fk_task_parent FOREIGN KEY (tsk_parent_id) REFERENCES public.task (tsk_id_in) ON DELETE SET NULL
)
	TABLESPACE tb_cloud_data;



CREATE TABLE public.task_menu
(
	mnu_id_in            INTEGER NOT NULL ,
	tsk_id_in            INTEGER NOT NULL ,
CONSTRAINT  XPKTasks_Menus PRIMARY KEY (mnu_id_in,tsk_id_in) USING INDEX TABLESPACE TB_CLOUD_INDEX ,
CONSTRAINT FK_MenuTask FOREIGN KEY (mnu_id_in) REFERENCES public.menu (mnu_id_in) ON DELETE CASCADE,
CONSTRAINT FK_TaskMenu FOREIGN KEY (tsk_id_in) REFERENCES public.task (tsk_id_in) ON DELETE CASCADE
);


CREATE TABLE public.role
(
	rol_id_in            INTEGER NOT NULL ,
	rol_parent_id        INTEGER NULL ,
	rol_description_ch   VARCHAR(64) NOT NULL ,
	rol_default_bt       BOOLEAN DEFAULT  FALSE  NULL ,
CONSTRAINT  XPKRoles PRIMARY KEY (rol_id_in) USING INDEX TABLESPACE TB_CLOUD_INDEX ,
CONSTRAINT FK_Role_ParentRole FOREIGN KEY (rol_parent_id) REFERENCES public.role (rol_id_in) ON DELETE SET NULL
)
	TABLESPACE tb_cloud_data;


Hope that helps a little!!!!
TYA

link publish delete flag offensive edit
Your reply
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!

[hide preview]

Question tools

Follow

RSS

Stats

Asked: 2008-11-13 19:14:30 +0800

Seen: 1,815 times

Last updated: Mar 02

Support Options
  • Email Support
  • Training
  • Consulting
  • Outsourcing
Learn More