/*
* Embedded Jopr Project
* Copyright (C) 2006-2009 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.jboss.on.embedded.ui;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Map;
import java.util.HashMap;
import javax.faces.component.UIComponent;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.domain.configuration.definition.ConfigurationDefinition;
import org.rhq.core.domain.measurement.Availability;
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceCategory;
import org.rhq.core.domain.resource.ResourceCreationDataType;
import org.rhq.core.domain.resource.ResourceType;
import org.richfaces.component.UITree;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.on.embedded.manager.ResourceManager;
import org.jboss.on.embedded.manager.ResourceManagerFactory;
import org.jboss.on.embedded.ui.nav.BaseTreeNode;
import org.jboss.on.embedded.ui.nav.DummyTreeNode;
import org.jboss.on.embedded.ui.nav.JONTreeNode;
import org.jboss.on.embedded.ui.nav.PlatformResourceTreeNode;
import org.jboss.on.embedded.ui.nav.ResourceTypeTreeNode;
import org.jboss.on.embedded.ui.nav.TreeNodeWithResource;
/**
* NavigationAction creates the navigation objects.
*
* @author Jessica Sant
* @author Charles Crouch
* @author Ian Springer
*/
// TODO (ips): Remove obsolete commented-out code.
// TODO: Go through this class and try to ensure the minimum amount of things are public.
// Most actions should be dealing with CommonActionUtil for getting hold of resources etc.
@Name("navigationAction")
@Scope(ScopeType.SESSION)
public class NavigationAction implements Serializable
{
static interface Tabs {
String CONFIGURATION = "configuration";
String METRIC = "metric";
String OPERATION = "operation";
String CONTENT = "content";
}
private Set<JONTreeNode> expandedYet = new HashSet<JONTreeNode>();
private int openDepth = 0;
public int getOpenDepth()
{
return this.openDepth;
}
public void setOpenDepth(int n)
{
this.openDepth = n;
}
private static final Log log = LogFactory.getLog(NavigationAction.class);
private Object navigationState;
public Object getNavigationState()
{
return navigationState;
}
public void setNavigationState(Object navigationState)
{
this.navigationState = navigationState;
}
private ResourceManager getManager()
{
return ResourceManagerFactory.resourceManager();
}
@In(value = "rootNode", create = true)
private JONTreeNode rootNode;
// Its only SummaryAction.getResourceTypePath which needs this to be public
// This returns a DummyTreeNode so its generally not going to be useful to other clients
public JONTreeNode getRootNode()
{
return rootNode;
}
// @Out(value = "navTree", required = false, scope = ScopeType.EVENT)
// public HtmlTree getNavTree()
// {
//// if (navTree == null)
//// {
//// navTree = buildNavTree();
//// }
// return navTree;
// }
//
// @In(value = "navTree", required = false, scope = ScopeType.EVENT)
// public void setNavTree(HtmlTree navTree)
// {
// this.navTree = navTree;
// }
//
// private HtmlTree navTree;
// @Factory("navTree")
// public HtmlTree buildNavTree()
// {
// // <rich:tree switchType="ajax" value="#{navigationAction.rootNode}" var="item" nodeFace="all">
// HtmlTree tree = (HtmlTree) getApplication().createComponent(HtmlTree.COMPONENT_TYPE);
// //tree.setId(getUniqueId());
// //HtmlTree tree = new HtmlTree();
//
// tree.setSwitchType("ajax");
// tree.setVar("item");
// tree.setNodeFace("all");
// ValueBinding valueBinding = getApplication().createValueBinding("#{navigationAction.rootNode}");
// tree.setValueBinding("value", valueBinding);
//
// //<rich:treeNode type="all">
// HtmlTreeNode node = (HtmlTreeNode) getApplication().createComponent(HtmlTreeNode.COMPONENT_TYPE);
// node.setId(getUniqueId());
// //HtmlTreeNode node = new HtmlTreeNode();
//
// node.setType("all");
//
// //<s:link view="/secure/summary.xhtml" propagation="end">#{item.name}
// HtmlLink link = (HtmlLink) getApplication().createComponent(HtmlLink.COMPONENT_TYPE);
// link.setId(getUniqueId());
// //HtmlLink link = new HtmlLink();
//
// link.setView("/secure/summary.xhtml");
// link.setPropagation("end");
// valueBinding = getApplication().createValueBinding("#{item.name}");
// link.setValueBinding("value", valueBinding);
//
// // <f:param name="path" value="#{item.path}"/>
// UIParameter param = (UIParameter) getApplication().createComponent(UIParameter.COMPONENT_TYPE);
// param.setId(getUniqueId());
// //UIParameter param = new UIParameter();
//
// param.setName("path");
// valueBinding = getApplication().createValueBinding("#{item.path}");
// param.setValueBinding("value", valueBinding);
//
// link.getChildren().add(param);
// node.getChildren().add(link);
// tree.getChildren().add(node);
//
// return tree;
// }
//
// private Application getApplication()
// {
// return FacesContext.getCurrentInstance().getApplication();
// }
//
// private int counter = 0;
//
// /**
// * Returns an id which is unique to this *instance* of the class.
// * Used to supply id's to the JSF components we create programmatically
// * @return
// */
// protected String getUniqueId()
// {
// return "jon_treeid_" + counter++;
// }
// private HtmlPanelGrid navPanelGrid;
// public HtmlPanelGrid getNavPanelGrid()
// {
// if (navPanelGrid == null)
// {
// Application application = FacesContext.getCurrentInstance().getApplication();
// navPanelGrid = (HtmlPanelGrid) application.createComponent(HtmlPanelGrid.COMPONENT_TYPE);
//
// TreeWidgetFactory factory = new TreeWidgetFactory(application);
// factory.buildTree(navPanelGrid);
// }
// return navPanelGrid;
// }
//
// public void setNavPanelGrid(HtmlPanelGrid navPanelGrid)
// {
// this.navPanelGrid = navPanelGrid;
// }
// session scoped variable holding the path of the most recently
// selected entry from the navigation
private String currentPath;
public String getCurrentPath()
{
return currentPath;
}
public void setCurrentPath(String path)
{
this.currentPath = path;
}
/**
* loads the breadcrumbs for the node specified by the currentPath variable
*
* @return the breadcrumbs (descending list of parent to children) for this node
*/
public List<JONTreeNode> getBreadCrumbs()
{
JONTreeNode selectedNode = getSelectedNode();
// add the current node
List<JONTreeNode> breadcrumbs = new ArrayList();
breadcrumbs.add(selectedNode);
JONTreeNode parent = selectedNode.getParent();
while (parent != null)
{
// make sure the most recently added parent ends up at the front of this list
breadcrumbs.add(0, parent);
parent = parent.getParent();
}
// remove the first breadcrumb node, if its the dummy tree node (which it should be
if (breadcrumbs.get(0) instanceof DummyTreeNode)
{
breadcrumbs.remove(0);
}
return breadcrumbs;
}
// @TODO if isAvailable is moved to CommonActionUtil, the methods below for enabling tabs should
// probably move there too or just move it to a Tab related class
public Map<String, Boolean> getEnabledTabs()
{
Map<String, Boolean> enabledTabs = new HashMap<String, Boolean>(4);
JONTreeNode currentNode = getSelectedNode();
if (currentNode instanceof TreeNodeWithResource)
{
Resource resource = ((TreeNodeWithResource)currentNode).getResource();
if (hasConfiguration(resource))
enabledTabs.put(Tabs.CONFIGURATION, true);
if (hasMetrics(resource))
enabledTabs.put(Tabs.METRIC, true);
if (resource.getResourceType().getCategory() != ResourceCategory.PLATFORM && hasOperations(resource))
enabledTabs.put(Tabs.OPERATION, true);
if (isContentBacked(resource))
enabledTabs.put(Tabs.CONTENT, true);
}
return enabledTabs;
}
private boolean isContentBacked(Resource resource)
{
return (resource.getResourceType().getCreationDataType().equals(ResourceCreationDataType.CONTENT));
}
private boolean hasOperations(Resource resource)
{
List operations = getManager().getOperationsForResource(resource);
return (!operations.isEmpty());
}
private boolean hasMetrics(Resource resource)
{
Set metrics = resource.getResourceType().getMetricDefinitions();
return (metrics != null && !metrics.isEmpty());
}
private boolean hasConfiguration(Resource resource)
{
ConfigurationDefinition resourceConfigDef = resource.getResourceType().getResourceConfigurationDefinition();
return (resourceConfigDef != null && !resourceConfigDef.getPropertyDefinitions().isEmpty());
}
// @TODO Another method that we should move to CommonActionUtil. I also think there should be a way
// @TODO to combine this with isAvailable, so mostly have the calls to isAvailable call this, and check
// @TODO against a string instead of a boolean, and it shouldn't need to have Resource as a parameter.
/**
* gets the available status for the current resource being viewed
*
* @return UP, DOWN or UNKNOWN (or SUMMARY if this isn't a ResourceTreeNode)
*/
public String getAvailableStatus()
{
String status = "SUMMARY";
JONTreeNode currentNode = getSelectedNode();
if (currentNode instanceof TreeNodeWithResource)
{
Resource resource = ((TreeNodeWithResource)currentNode).getResource();
return getAvailableStatus(resource);
}
return status;
}
/**
* gets the availability status for the given resource
*
* @param resource the inventoried resource being queried
* @return UP, DOWN or UNKNOWN
*/
public String getAvailableStatus(Resource resource)
{
Availability availability = getManager().getAvailability(resource);
AvailabilityType type = availability.getAvailabilityType();
//noinspection UnnecessaryLocalVariable
String status = (type != null) ? type.getName() : "UNKNOWN";
return status;
}
/**
* true if the given node is the same as the currently selected node, false otherwise
*
* @param node the node to test if its the same as the currently selected node
* @return true if the given node is the same as the currently selected node, false otherwise
*/
public boolean equalsSelectedNode(JONTreeNode node)
{
JONTreeNode currentNode = getSelectedNode();
return node.equals(currentNode);
}
// TODO figure out where path is getting set to / when viewing the root page
// its getting set in the redirect to index.xhtml
// TODO could consider renaming this getCurrentNavigationNode()
public JONTreeNode getSelectedNode()
{
if (log.isTraceEnabled())
log.trace("Finding node for path [" + getCurrentPath() + "]...");
JONTreeNode selectedNode = getRootNode().findNode(getCurrentPath());
ResourceManager resourceManager = ResourceManagerFactory.resourceManager();
if (selectedNode == null)
{
log.debug("Unable to find node with path [" + this.currentPath + "]; defaulting to platform node...");
// TODO (ccrouch): I'm seeing this on login now. Comment out for now.
// String errorMsg = "Unable to find node with path [" + this.currentPath + "]; defaulting to platform node...";
// LOG.error(errorMsg);
// this.facesMessages.add(FacesMessage.SEVERITY_ERROR, errorMsg);
this.currentPath = getResourcePath(resourceManager.getPlatform());
selectedNode = getRootNode().findNode(this.currentPath);
}
return selectedNode;
}
// TODO move to CommonActionUtil
public boolean isAvailable(Resource r)
{
Availability availability = getManager().getAvailability(r);
return availability.getAvailabilityType() == AvailabilityType.UP;
}
// TODO look at making this private or used by only CommonActionUtil
public JONTreeNode findNode(String path)
{
return getRootNode().findNode(path);
}
public String getResourcePath(Integer resourceId)
{
// this just happens to be the current implementation for generating the path
// if we wanted to change it, we just need to change the methods in this class
// and ResourceTreeNode
// TODO this could call a static method on ResourceTreeNode to keep this
// implementation better encapsulated
return String.valueOf(resourceId);
}
public String getResourcePath(Resource resource)
{
return getResourcePath(resource.getId());
}
// TODO consider have this return TreeNodeWithResourceType
public ResourceTypeTreeNode findNodeByResourceTypeAndParent(ResourceType resourceType, JONTreeNode parentResourceNode)
{
return ((BaseTreeNode)parentResourceNode).findNodeByType(resourceType);
}
public Boolean openNodeAdvisor(UITree tree)
{
JONTreeNode data = (JONTreeNode)(tree.getTreeNode().getData());
if (!expandedYet.contains(data) && isWithinNStepsOfPlatform(this.openDepth - 1, data))
{
expandedYet.add(data);
return Boolean.TRUE;
}
else
{
return null;
}
}
private Boolean isWithinNStepsOfPlatform(int n, JONTreeNode node)
{
if ((n < 0) || node == null)
{
return Boolean.FALSE;
}
else
{
return (node instanceof PlatformResourceTreeNode) || isWithinNStepsOfPlatform(n - 1, node.getParent());
}
}
// unfortunately can't tell whether this is an open or close event
// so we have to re-do the childMap in all cases
public void changeExpandListener(org.richfaces.event.NodeExpandedEvent event)
{
UIComponent comp = event.getComponent();
UITree tree = (UITree)comp;
JONTreeNode jonTreeNode = (JONTreeNode)(tree.getTreeNode().getData());
jonTreeNode.reInitializeChildrenMap();
}
// /**
// * Sorts the list of datasources by the given column name in ascending or descending order.
// * If the given column name doesn't match, the list is not sorted
// *
// * @param column the column to sort by
// * @param ascending true if the list should be sorted in ascending order, false if in descending order
// * @param listToSort the Resource list to be sorted
// */
// //TODO: need to figure out where this sort method fits best
// public void sort(final String column, final boolean ascending, List<ResourceListItem> listToSort)
// {
// LOG.info("sorting - " + column + ", " + ascending);
// final NavigationAction navigationAction = (NavigationAction) Contexts.lookupInStatefulContexts( "navigationAction" );
// Comparator<ResourceListItem> comparator = new Comparator<ResourceListItem>()
// {
// public int compare(ResourceListItem c1, ResourceListItem c2)
// {
// if (column == null)
// {
// return 0;
// }
// if (column.equals(navigationAction.getColumnJndiName()))
// {
// return ascending ?
// c1.getName().compareTo(c2.getName()):
// c2.getName().compareTo(c1.getName());
// }
// else if (column.equals(navigationAction.getColumnType()))
// {
// return ascending ?
// c1.getResource().getResourceType().getName().compareTo(c2.getResource().getResourceType().getName()):
// c2.getResource().getResourceType().getName().compareTo(c1.getResource().getResourceType().getName());
// }
// if (column.equals(navigationAction.getColumnStatus()))
// {
// return ascending ?
// c1.getAvailability().compareTo(c2.getAvailability()):
// c2.getAvailability().compareTo(c1.getAvailability());
// }
// else return 0;
// }
// };
// Collections.sort(listToSort, comparator);
// }
}