/**
* $Id: TreeViewerBean.java,v 1.22 2005/11/09 23:27:45 rt94277 Exp $
* Copyright 2005 Sun Microsystems, Inc. All
* rights reserved. Use of this product is subject
* to license terms. Federal Acquisitions:
* Commercial Software -- Government Users
* Subject to Standard License Terms and
* Conditions.
*
* Sun, Sun Microsystems, the Sun logo, and Sun ONE
* are trademarks or registered trademarks of Sun Microsystems,
* Inc. in the United States and other countries.
*/
package com.sun.portal.admin.console.desktop;
import com.sun.portal.admin.common.DesktopConstants;
import com.sun.portal.admin.common.AttrOptionConstants;
import com.sun.portal.admin.common.util.AdminClientUtil;
import com.sun.portal.admin.console.common.PortalBaseBean;
import com.sun.web.ui.model.Option;
import com.sun.web.ui.component.Tree;
import com.sun.web.ui.component.TreeNode;
import com.sun.web.ui.component.ImageComponent;
import com.sun.web.ui.component.Hyperlink;
import java.util.Map;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.Collections;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.net.URLEncoder;
import java.io.UnsupportedEncodingException;
import javax.faces.event.ActionEvent;
import javax.faces.context.FacesContext;
import javax.faces.el.VariableResolver;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.management.ObjectName;
/**
* Bean that handles the Navigation frame under the Desktop Manager. This bean
* is responsible for handling and managing the data and events related to the
* tree and other objects in the navigation frame.
*
* NOTE: This Bean is using dynamic binding. The contents of the tree, TreeNode
* objects are created at runtime. Due to a limitation in JSF/Lockhart, for
* components that use dynamic binding the get and set methods are only invoked
* once on the component in the session. Hence the only way to get the content
* of the tree refreshed after initial get/set is by the method invocation of
* the viewChange event. For page refresh operations this is achieved by using
* Javascript to set values to hidden fields and submitting the form to ensure
* that the viewChange method is invoked on the bean.
*/
public class TreeViewerBean extends PortalBaseBean implements DesktopConstants {
/**
* String that identifies the DOM tree of the merged DP Document
*/
public static final String TYPE_PHYSICAL_TREE = "0";
// Tree form Ids
private static final String TREE_ID = "cTree";
private static final String TREE_FORM_ID = "TreeViewer";
// Icon image urls
private static final String ROOT_CONTAINER_ICON = "/images/dtree/root_container.gif";
private static final String VISIBLE_CONTAINER_ICON = "/images/dtree/container.gif";
private static final String VISIBLE_CHANNEL_ICON = "/images/dtree/channel.gif";
private static final String VISIBLE_PORTLET_CHANNEL_ICON = "/images/dtree/local_portlet_channel.gif";
private static final String VISIBLE_REMOTE_PORTLET_CHANNEL_ICON = "/images/dtree/remote_portlet_channel.gif";
private static final String INVISIBLE_CONTAINER_ICON = "/images/dtree/container_notvisible.gif";
private static final String INVISIBLE_CHANNEL_ICON = "/images/dtree/channel_notvisible.gif";
private static final String INVISIBLE_PORTLET_CHANNEL_ICON = "/images/dtree/local_portlet_channel_notvisible.gif";
private static final String INVISIBLE_REMOTE_PORTLET_CHANNEL_ICON = "/images/dtree/remote_portlet_channel_notvisible.gif";
private static final String INDENT = "- ";
private Tree cTree = null;
private TreeNode rootTreeNode = null;
private TreeNode selectedNode = null;
private String selectedChannel = null;
private String currentView = null;
private List viewTypes = Collections.EMPTY_LIST;
private Set topNodes = Collections.EMPTY_SET;
/**
* Constructor
*/
public TreeViewerBean() {
log(Level.FINEST, "Tree Bean Constructor Invoked...");
}
/**
* Fetches the contents for the View Menu
*
* @return An option array of items to be displayed in the menu
*/
public List getViewTypes() {
if (!viewTypes.isEmpty()) {
return viewTypes;
}
String defaultChannel = "";
try {
Object [] params = { getCurrentDN() };
String [] signature = { "java.lang.String" };
String methodName = "getTopLevelChannels";
// Get the Display Profile MBean Object Name
ObjectName objName =
AdminClientUtil.getDisplayProfileMBeanObjectName(
AdminClientUtil.DEFAULT_DOMAIN,
getPortalId());
// Invoke the get method on the portal Desktop MBean to fetch the
// list of top level container nodes available at the current DN
topNodes = (Set) getMBeanServerConnection().invoke(objName,
methodName,
params,
signature);
// Get the Portal Object Name
ObjectName pObjName = AdminClientUtil.getPortalMBeanObjectName(
AdminClientUtil.DEFAULT_DOMAIN,
getPortalId());
Set attrNames = new HashSet();
attrNames.add("DefaultChannel"); // TODO - Pick from constants file
Map options = new HashMap();
options.put(AttrOptionConstants.OPT_COMPONENT, "desktop");
options.put("operation", "get");
options.put(AttrOptionConstants.OPT_ATTR_NAMES, attrNames);
if (!((String)getCurrentDN()).equals(GLOBAL_LOCATION_DN) ) {
options.put(AttrOptionConstants.OPT_DN, getCurrentDN());
}
Object [] prms = { options };
String [] sig = { "java.util.Map" };
methodName = "getAttributes";
// Invoke the method to fetch the default channel name at the
// current DN for this portal
Map attrVals = (Map) getMBeanServerConnection().invoke(pObjName,
methodName,
prms,
sig);
List values = (List) attrVals.get(DEFAULT_CHANNEL);
if (values != null && !values.isEmpty()) {
String val = (String) values.get(0);
if (val != null) {
defaultChannel = val.trim();
}
}
} catch (Exception e) {
log(Level.SEVERE, "TreeViewerBean.getViewTypes(): Error Fetching Top Level Container set", e);
}
log(Level.FINEST, "Top Level Container Set: " + topNodes);
log(Level.FINEST, "Default Channel Value: " + defaultChannel);
String pTreeLbl = getI18NString("label.dom.tree");
Option pTreeOpt = new Option(TYPE_PHYSICAL_TREE, pTreeLbl);
String line = "========";
StringBuffer dViewsLabel = new StringBuffer();
dViewsLabel.append(line).append(" ")
.append(getI18NString("label.visual.tree"))
.append(" ").append(line);
Option dViewsOpt = new Option("", dViewsLabel.toString());
dViewsOpt.setDisabled(true);
// Add both views to the list of viewTypes
// Initialize the viewTypes
viewTypes = new ArrayList();
viewTypes.add(pTreeOpt);
viewTypes.add(dViewsOpt);
// Initialize a flag to check if default channel exists in set of
// top level containers
boolean exists = false;
if (defaultChannel != null) {
String dContainerLabel = INDENT + getTruncatedString(defaultChannel)
+ " [" + getI18NString("label.view.default") + "]";
Option dContainerOpt = new Option(defaultChannel, dContainerLabel);
// Check if the default channel exists in the list of top level
// containers.
exists = topNodes.contains(defaultChannel);
// That means the value set to the default channel service attribute
// is invalid and is available at the current DN. So disable this
// option in the viewType menu
dContainerOpt.setDisabled(!exists);
// Add this Default Channel Option to the viewTypes menu items
viewTypes.add(dContainerOpt);
} else {
// If its null set it to empty string
defaultChannel = "";
}
// Minimum of two options Physical and Visual tree are always displayed
// If the top level container list is not empty we will populate the
// viewOptions array with the names of the containers
Iterator itr = topNodes.iterator();
while (itr.hasNext()) {
String cName = (String) itr.next();
if (!cName.equals(defaultChannel)) {
String cVisibleName = INDENT + getTruncatedString(cName);
Option containerOption = new Option(cName, cVisibleName);
containerOption.setTooltip(cName);
viewTypes.add(containerOption);
}
}
return viewTypes;
}
private String getTruncatedString(String str) {
String output;
// If the name of the container is more than 25 chars, prune it
if (str.length() > 25) {
output = str.substring(0, 25) + "...";
} else {
output = str;
}
return output;
}
private void initCurrentView() {
// Initialize currentView to physical tree
currentView = TYPE_PHYSICAL_TREE;
// Get the selected container sesssion attribute to decide the value
// that should be set for the currentView
String lastView = (String) getSessionAttribute(ATTR_SELECTED_TREE_CONTAINER);
if (lastView == null) {
// Set to empty string. The first visit to the tree. So no value
// set into the session.
lastView = "";
}
log(Level.FINEST, "Last Selected View: " + lastView);
// Iterate through all options to check if the selectedContainer is one
// of the top level containers.
Iterator itr = viewTypes.iterator();
while (itr.hasNext()) {
// Get the name of the container in this option
Option option = (Option) itr.next();
String containerName = (String) option.getValue();
// If current option is not disabled and containerName is same as
// selectedContainer value set currentView to it and end loop
if (!option.isDisabled() &&
containerName.equals(lastView)) {
// If equals then set it as currentView and break out of loop
currentView = lastView;
break;
}
}
// After looping through all optios if the currentView is still set to
// physical tree then try to set currentView to Default Channel Value
// if its not disabled.
if (currentView.equals(TYPE_PHYSICAL_TREE) &&
!lastView.equals(TYPE_PHYSICAL_TREE)) {
if (viewTypes.size() > 2) {
Option option = (Option) viewTypes.get(2);
if (!option.isDisabled()) {
currentView = (String) option.getValue();
}
}
}
}
/**
* Gets the view selected by the user
*
* @return A string representing the current view
*/
public String getCurrentView() {
// If currentView is not initialized then maybe the getViewTypes is not
// already invoked. Invoke getViewTypes
if (viewTypes.isEmpty()) {
getViewTypes();
initCurrentView();
}
return currentView;
}
/**
* Sets the view item selected by the user
*
* @param viewType A string representing the current selected view
*/
public void setCurrentView(String viewType) {
currentView = viewType;
}
/**
* This method gets the value for the string that represents the top DPNode
* that is displayed on the tree. A null is returned if the tree cannot be
* displayed
* @return Name of the DPNode represented by the top level node in the tree
*/
public String getDefaultChannel() {
String channel;
// If cTree is null invoke resetTree. That will initialize it
if (cTree == null) {
log(Level.FINEST, "Tree is Null. Invoked createTree()");
cTree = createTree(getCurrentView());
}
if (selectedNode != null) {
channel = selectedNode.getText();
} else {
channel = cTree.getText();
}
String encodedName = channel;
if (channel != null) {
try {
encodedName = URLEncoder.encode(channel, DEFAULT_CHARSET);
} catch (UnsupportedEncodingException uee) {
encodedName = channel;
}
}
return encodedName;
}
/**
* This method gets the value for the string that Client HTML Id for the
* tree node that is currently selected or set into the session. If the
* node does not exist in the current tree it will return a null
* @return Client Id of the selected tree node as a string
*/
public String getDefaultChannelNodeId() {
// If cTree is null invoke resetTree. That will initialize it
if (cTree == null) {
log(Level.FINEST, "Tree is Null. Invoked createTree()");
cTree = createTree(getCurrentView());
}
FacesContext context = FacesContext.getCurrentInstance();
// Initialize this to a element id thats does not exist in the page
String nodeId = "**";
// If the selected node is not null get the nodes element id
if (selectedNode != null) {
nodeId = selectedNode.getClientId(context);
}
return nodeId;
}
/**
* Prepares are Tree Object representing the contents of a users desktop as
* TreeNodes. Each node representing either a Channel or a Container.
*
* @return A Tree Object representing the Desktop content
*/
public Tree getTree() {
log(Level.FINEST, "Get Tree Invoked");
if (cTree == null) {
cTree = createTree(getCurrentView());
}
return cTree;
}
/**
* Receives the Tree object from JSF and sets it into the bean
*
* @param tree Tree Object reperenting the tree from the form submission
*/
public void setTree(Tree tree) {
log(Level.FINEST, "Set Tree Invoked");
cTree = tree;
}
/**
* Handles the event that occurs when the user changes the selected viewmenu
*
* @param event ActionEvent
*/
public void viewChange(ActionEvent event) {
log(Level.FINEST, "Value of currentView : " + currentView);
// If the selected view is not Physical tree and it does not exist
// in the list of top level containers then most likely the currentView
// field is out of sync. Call initCurrentView to reset its value
if (!currentView.equals(TYPE_PHYSICAL_TREE)) {
if (!topNodes.contains(currentView)) {
initCurrentView();
log(Level.FINEST, "CurrentView value updated to :"+currentView);
}
}
// Reset the tree content
cTree = createTree(getCurrentView());
}
private Tree createTree(String type) {
log(Level.FINEST, "Attempting to Create Tree of type: " + type);
log(Level.FINEST, "Fetching Data for DN: " + getCurrentDN());
// Set the selected viewtype which is name of container into session
setSessionAttribute(ATTR_SELECTED_TREE_CONTAINER, currentView);
// Get the selected node from the session attribute
selectedChannel = (String) getSessionAttribute(ATTR_SELECTED_TREE_CHANNEL);
// If its not available in the session then try to get it from the
// EditProperties bean
if (selectedChannel == null) {
FacesContext context = FacesContext.getCurrentInstance();
VariableResolver vr = context.getApplication().getVariableResolver();
Object obj = (Object) vr.resolveVariable(context, "EditPropertiesBean");
if ((obj != null) && (obj instanceof EditPropertiesBean)) {
EditPropertiesBean epropsbean = (EditPropertiesBean) obj;
selectedChannel = epropsbean.getChannelName();
}
}
// Initialize a new tree
Tree tree = new Tree();
tree.setClientSide(true);
tree.setExpanded(true);
tree.setText(getI18NString("label.content.unavailable"));
tree.setId(TREE_ID);
tree.setTransient(true);
// Setting the params and signature
Object[] params;
String[] signature;
try {
String methodName = null;
if (type.equals(TYPE_PHYSICAL_TREE)) {
params = new Object [] { getCurrentDN() };
signature = new String [] { "java.lang.String" };
methodName = "getPhysicalHierarchy";
} else {
log(Level.FINEST, "Root Container: " + currentView);
params = new Object [] { getCurrentDN(), currentView };
signature = new String [] { "java.lang.String",
"java.lang.String" };
methodName = "getVisualHierarchy";
}
// Get the Display Profile MBean Object Name
ObjectName objName =
AdminClientUtil.getDisplayProfileMBeanObjectName(
AdminClientUtil.DEFAULT_DOMAIN,
getPortalId());
// reset the rootTreeNode to null
rootTreeNode = null;
// Invoke the get method on the portal Desktop MBean
Object [] rootNode = (Object []) getMBeanServerConnection().invoke(
objName, methodName,
params, signature);
if (rootNode != null) {
// Populate the treeNodes with the data from the mbean
populateTree(null, rootNode);
// Because of a limitation/bug in JSF the following is needed
List topLevelNodes = new ArrayList();
topLevelNodes.addAll(rootTreeNode.getChildren());
// if the treeNodes are directly added to tree there is data loss.
tree.setText(rootTreeNode.getText());
tree.getChildren().addAll(topLevelNodes);
tree.getFacets().put(tree.IMAGE_FACET_KEY,
getNodeImage(TYPE_ROOT_CONTAINER_NODE));
Hyperlink tLink = getTreeNodeLink(rootTreeNode.getText());
// Set the onClick method to clear the highlight of child nodes
// This is a hack to workaround LH tree issue
tLink.setOnClick("clearAllHighlight('TreeViewer:cTree');");
// If the node name is _root (DP XML Tree) localize this string
// to DP_ROOT
if(tLink.getText().equals("_root")) {
tLink.setText(getI18NString("edit.properties.dproot.label"));
}
tree.getFacets().put(tree.CONTENT_FACET_KEY,tLink);
if (selectedNode != null) {
tree.selectTreeNode(selectedNode);
}
} else {
// Log warning that the mbean method returned bad data
log(Level.WARNING, "MBean Returned a NULL");
}
} catch (Exception e) {
log(Level.SEVERE, "TreeViewerBean.createTree(): Error Fetching Data", e);
}
return tree;
}
/**
* Using recursion, prepares a tree structure of TreeNode objects from the
* data that is passed to it. It picksup the current node which is an Object
* Array. It prepares a TreeNode out of the data in the Object Array.
* The last element in the Object Array can be an ArrayList. Each element in
* this ArrayList represents a child to the current node.
*
* @param parent A TreeNode object representing the parent to current node
* @param currNode An Object Array. This method expects the Object array to
* have three elements.
* currNode[0] - A String representing the name of the current node
* currNode[1] - A String representing the type of the current node
* currNode[3] - An ArrayList representing the children of the current
* node.
*/
private void populateTree(TreeNode parent, Object[] currNode) {
TreeNode currentTreeNode = null;
if (currNode != null && currNode.length == 3) {
// Get the components of this node
String name = (String) currNode[0];
String type = (String) currNode[1];
// Create and add TreeNode representing the current
// node to the parent of the current node.
currentTreeNode = createTreeNode(name, type);
log(Level.FINEST, "Created Node: " + currentTreeNode.getText());
// If the parent node does not have a parent its parent.
// We are trying to add this newly created tnode is the first object
// in the tree
if (parent == null) {
// So make the newly created tnode the rootTreeNode.
rootTreeNode = currentTreeNode;
} else {
// Else add the newly created tnode as a child of the parent
// TreeNode
log(Level.FINEST, "Adding: " + currentTreeNode.getText() +
" To Parent: " + parent.getText());
parent.getChildren().add(currentTreeNode);
}
// If there are child nodes under the current node process them
List children = (List) currNode[2];
if (children != null && !children.isEmpty()) {
Iterator itr = children.iterator();
while (itr.hasNext()) {
Object[] childData = (Object []) itr.next();
populateTree(currentTreeNode, childData);
}
} else {
log(Level.FINEST, "No children for Current Node: " +
currentTreeNode.getText());
}
}
}
/**
* Creates a TreeNode object that can be used to populate a tree
*
* @param name A String representing the Text of this TreeNode
* @param type A String value representing the type of this node
* @return TreeNode object
*/
private TreeNode createTreeNode(String name, String type) {
TreeNode tnode = new TreeNode();
// Suffix C_ to take care of id issues if channelname starts with number
tnode.setId("C_" + name.replace('/', '_'));
tnode.setText(name);
Map nodeFacets = tnode.getFacets();
nodeFacets.put(tnode.IMAGE_FACET_KEY, getNodeImage(type));
nodeFacets.put(tnode.CONTENT_FACET_KEY, getTreeNodeLink(name));
// Expand the node if its a container node
if (Integer.parseInt(type) < 3) {
tnode.setExpanded(true);
}
if (name.equalsIgnoreCase(selectedChannel)) {
selectedNode = tnode;
}
return tnode;
}
/**
* Used by the createTreeNode method to create an ImageComponent thats
* embedded into the TreeNode as a facet
*
* @param type A String representing the type of the Node.
* @return A new ImageComponent Object
*/
private ImageComponent getNodeImage(String type) {
String iconUrl = "";
String altText = "";
boolean isDesktopView = !currentView.equals(TYPE_PHYSICAL_TREE);
if (type.equals(TYPE_ROOT_CONTAINER_NODE)) {
iconUrl = ROOT_CONTAINER_ICON;
altText = getI18NString("alt.root.container");
} else if (type.equals(TYPE_VISIBLE_CHANNEL_NODE)) {
iconUrl = VISIBLE_CHANNEL_ICON;
if (isDesktopView) {
altText = getI18NString("alt.visible.channel");
} else {
altText = getI18NString("alt.channel");
}
} else if (type.equals(TYPE_VISIBLE_PORTLET_CHANNEL_NODE)) {
iconUrl = VISIBLE_PORTLET_CHANNEL_ICON;
if (isDesktopView) {
altText = getI18NString("alt.visible.portlet.channel");
} else {
altText = getI18NString("alt.portlet.channel");
}
} else if (type.equals(TYPE_VISIBLE_REMOTE_PORTLET_CHANNEL_NODE)) {
iconUrl = VISIBLE_REMOTE_PORTLET_CHANNEL_ICON;
if (isDesktopView) {
altText = getI18NString("alt.visible.remote.portlet.channel");
} else {
altText = getI18NString("alt.remote.portlet.channel");
}
} else if (type.equals(TYPE_VISIBLE_CONTAINER_NODE)) {
iconUrl = VISIBLE_CONTAINER_ICON;
if (isDesktopView) {
altText = getI18NString("alt.visible.container");
} else {
altText = getI18NString("alt.container");
}
} else if (type.equals(TYPE_INVISIBLE_CHANNEL_NODE)) {
iconUrl = INVISIBLE_CHANNEL_ICON;
altText = getI18NString("alt.invisible.channel");
} else if (type.equals(TYPE_INVISIBLE_PORTLET_CHANNEL_NODE)) {
iconUrl = INVISIBLE_PORTLET_CHANNEL_ICON;
altText = getI18NString("alt.invisible.portlet.channel");
} else if (type.equals(TYPE_INVISIBLE_REMOTE_PORTLET_CHANNEL_NODE)) {
iconUrl = INVISIBLE_REMOTE_PORTLET_CHANNEL_ICON;
altText = getI18NString("alt.invisible.remote.portlet.channel");
} else if (type.equals(TYPE_INVISIBLE_CONTAINER_NODE)) {
iconUrl = INVISIBLE_CONTAINER_ICON;
altText = getI18NString("alt.invisible.container");
}
ImageComponent img = new ImageComponent();
img.setId("Image");
img.setUrl(iconUrl);
img.setAlt(altText);
img.setToolTip(altText);
return img;
}
/**
* Used by the createTreeNode method to create a link that is set into the
* TreeNode.
* @param name A String representing the name of the TreeNode
* @return A newly created Hyperlink object
*/
private Hyperlink getTreeNodeLink(String name) {
// Prepare the display name for the node
String displayName = name;
int idx = name.lastIndexOf("/");
if (idx > -1) {
displayName = name.substring(idx + 1);
}
String encodedName = null;
try {
encodedName = URLEncoder.encode(name, DEFAULT_CHARSET);
} catch (UnsupportedEncodingException uee) {
encodedName = name;
}
Hyperlink hl = new Hyperlink();
hl.setId("Link");
hl.setToolTip(name);
hl.setUrl("/faces/desktop/DesktopObjectManager.jsp?" +
ATTR_SELECTED_CHANNEL_NAME + "=" + encodedName +
"&" + ATTR_NESTED_PROPERTY_NAME + "=null");
hl.setTarget("dmgrdata");
hl.setText(displayName);
/** Below logic is needed only if an action listener needs to be added
try {
Class actionListenerArgs[] = { ActionEvent.class };
FacesContext context = FacesContext.getCurrentInstance();
Application app = context.getApplication();
MethodBinding mb = app.createMethodBinding(
"#{TreeViewerBean.nodeClick}",
actionListenerArgs);
hl.setActionListener(mb);
} catch (Exception e) {
}
**/
return hl;
}
/**
* Gets a localized string from the resoucebundle
* @param key A key string
* @return A localized String for the key
*/
private String getI18NString(String key) {
return getLocalizedString("desktop", key);
}
}
|