/*
* NOTE: This copyright does *not* cover user programs that use HQ
* program services by normal system calls through the application
* program interfaces provided as part of the Hyperic Plug-in Development
* Kit or the Hyperic Client Development Kit - this is merely considered
* normal use of the program, and does *not* fall under the heading of
* "derived work".
*
* Copyright (C) [2004, 2005, 2006], Hyperic, Inc.
* This file is part of HQ.
*
* HQ is free software; you can redistribute it and/or modify
* it under the terms version 2 of the GNU General Public License as
* published by the Free Software Foundation. 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
package org.hyperic.hq.ui.action.resource.hub;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeSet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.hq.appdef.shared.AppdefEntityConstants;
import org.hyperic.hq.appdef.shared.AppdefEntityID;
import org.hyperic.hq.appdef.shared.AppdefEntityTypeID;
import org.hyperic.hq.appdef.shared.AppdefInventorySummary;
import org.hyperic.hq.appdef.shared.AppdefResourceTypeValue;
import org.hyperic.hq.appdef.shared.AppdefResourceValue;
import org.hyperic.hq.appdef.shared.InvalidAppdefTypeException;
import org.hyperic.hq.authz.server.session.AuthzSubject;
import org.hyperic.hq.authz.server.session.ResourceGroup;
import org.hyperic.hq.authz.shared.PermissionException;
import org.hyperic.hq.bizapp.shared.AppdefBoss;
import org.hyperic.hq.bizapp.shared.AuthzBoss;
import org.hyperic.hq.bizapp.shared.MeasurementBoss;
import org.hyperic.hq.events.AlertPermissionManager;
import org.hyperic.hq.measurement.MeasurementConstants;
import org.hyperic.hq.measurement.UnitsConvert;
import org.hyperic.hq.measurement.server.session.MeasurementTemplate;
import org.hyperic.hq.product.MetricValue;
import org.hyperic.hq.ui.Constants;
import org.hyperic.hq.ui.Portal;
import org.hyperic.hq.ui.WebUser;
import org.hyperic.hq.ui.action.BaseActionNG;
import org.hyperic.hq.ui.taglib.display.StringUtil;
import org.hyperic.hq.ui.util.RequestUtils;
import org.hyperic.hq.ui.util.SessionUtils;
import org.hyperic.util.config.InvalidOptionException;
import org.hyperic.util.pager.PageControl;
import org.hyperic.util.pager.PageList;
import org.hyperic.util.timer.StopWatch;
import org.hyperic.util.units.FormattedNumber;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ModelDriven;
/**
* An <code>Action</code> that sets up the Resource Hub portal.
*/
@Component(value = "resourceHubPortalActionNG")
@Scope(value = "prototype")
public class ResourceHubPortalActionNG extends BaseActionNG implements
ModelDriven<ResourceHubFormNG> {
private static final String BLANK_LABEL = "";
private static final String BLANK_VAL = "";
private static final String PLATFORM_KEY = "resource.hub.filter.PlatformType";
private static final String SERVER_KEY = "resource.hub.filter.ServerType";
private static final String SERVICE_KEY = "resource.hub.filter.ServiceType";
public static final int SELECTOR_GROUP_COMPAT = 1;
public static final int SELECTOR_GROUP_ADHOC = 2;
public static final int SELECTOR_GROUP_DYNAMIC = 3;
private static final int DEFAULT_ENTITY_TYPE = Constants.FILTER_BY_DEFAULT;
private static final int DEFAULT_RESOURCE_TYPE = -1;
private static final int DEFAULT_GROUP_TYPE = 1;
private static final String DEFAULT_RESOURCE_NAME = null;
private static final String SEPARATOR = " › ";
private static final String VIEW_ATTRIB = "Resource Hub View";
private static final String TYPE_ATTRIB = "Resource Hub Apppdef Type";
private static final String GRP_ATTRIB = "Resource Hub Group Type";
private final Log log = LogFactory.getLog(ResourceHubPortalActionNG.class
.getName());
@Autowired
private AppdefBoss appdefBoss;
@Autowired
private AuthzBoss authzBoss;
@Autowired
private MeasurementBoss measurementBoss;
@Autowired
private AlertPermissionManager alertPermissionManager;
private ResourceHubFormNG hubForm = new ResourceHubFormNG();
/**
* Set up the Resource Hub portal.
*/
public String execute() throws Exception {
setHeaderResources();
boolean prefChanged = false;
int sessionId = RequestUtils.getSessionId(getServletRequest())
.intValue();
// Set the view in the form
HttpSession session = getServletRequest().getSession();
WebUser user = SessionUtils.getWebUser(session);
PageControl pc = RequestUtils.getPageControl(getServletRequest());
String view = hubForm.getView();
if (!ResourceHubFormNG.LIST_VIEW.equals(view)
&& !ResourceHubFormNG.CHART_VIEW.equals(view)) { // Invalid
// view
view = null;
}
String prefView;
try {
prefView = user.getPreference(VIEW_ATTRIB);
} catch (InvalidOptionException e) {
prefView = ResourceHubFormNG.LIST_VIEW;
}
if (view == null) {
hubForm.setView(prefView);
} else if (!view.equals(prefView)) {
user.setPreference(VIEW_ATTRIB, view);
prefChanged = true; // Save new preference
}
String navHierarchy = null;
// find resources specified by entity type and potentially
// resource type. collect query parameters and replace invalid
// ones with defaults.
int entityType;
try {
String prefFFStr = user.getPreference(TYPE_ATTRIB);
entityType = Integer.parseInt(prefFFStr);
} catch (InvalidOptionException e) {
entityType = DEFAULT_ENTITY_TYPE;
}
Integer ff = hubForm.getFf();
if (ff == null || ff.intValue() == 0) {
ff = new Integer(entityType);
hubForm.setFf(ff);
} else if (ff.intValue() != entityType) {
entityType = ff.intValue();
user.setPreference(TYPE_ATTRIB, new Integer(entityType));
prefChanged = true; // Save new preference
}
// start the navHierarchy with the ff type
navHierarchy = StringUtil.toUpperCaseAt(
AppdefEntityConstants.typeToString(entityType), 0)
+ "s" + SEPARATOR;
// are we looking at a group? check now, because if we *are*
// looking at a group, we're going to override the entity
// type.
boolean isGroupSelected = isGroupSelected(entityType);
int resourceType = DEFAULT_RESOURCE_TYPE;
String ft = hubForm.getFt();
AppdefEntityTypeID aetid = null;
if (ff.intValue() != AppdefEntityConstants.APPDEF_TYPE_APPLICATION
&& ft != null && ft.length() > 0
&& !ft.equals(String.valueOf(DEFAULT_RESOURCE_TYPE))) {
try {
// compat groups use the entity id format for ft
aetid = new AppdefEntityTypeID(ft);
resourceType = aetid.getId().intValue();
// only compat groups specify ft as an entity id. in
// this case, the entity id's entity type overrides
// the one specified by ff (which is just
// APPDEF_TYPE_GROUP anyway).
entityType = aetid.getType();
} catch (InvalidAppdefTypeException e) {
// what we got from the menu was not an entity id at
// all, but rather an integer
resourceType = (new Integer(ft)).intValue();
}
if (aetid != null) {
String typeName = appdefBoss.findResourceTypeById(sessionId,
new AppdefEntityTypeID(ft)).getName();
navHierarchy += typeName;
}
} else {
hubForm.setFt(new Integer(resourceType).toString());
navHierarchy += "All "
+ StringUtil.toUpperCaseAt(
AppdefEntityConstants.typeToString(entityType), 0)
+ "s";
}
Integer g = hubForm.getG();
int groupType;
if (g == null || g.intValue() < SELECTOR_GROUP_COMPAT) {
try {
String grpTypeStr = user.getPreference(GRP_ATTRIB);
groupType = Integer.parseInt(grpTypeStr);
} catch (InvalidOptionException e) {
groupType = DEFAULT_GROUP_TYPE;
}
hubForm.setG(new Integer(groupType));
} else {
groupType = g.intValue();
user.setPreference(GRP_ATTRIB, new Integer(groupType));
prefChanged = true; // Save new preference
}
String jsInserted = getText("resource.hub.search.KeywordSearchText");
String resourceName = hubForm.getKeywords();
if (resourceName != null
&& (resourceName.equals("") || resourceName.equals(jsInserted))) {
resourceName = DEFAULT_RESOURCE_NAME;
hubForm.setKeywords(resourceName);
}
StopWatch watch = new StopWatch();
watch.markTimeBegin("findCompatInventory");
Integer gid = null;
int[] groupSubtype = null;
if (isGroupSelected) {
// as far as the backend is concerned, the resource type
// is the actual group type we want. our groupType just
// tells us compat or adhoc.
if (isCompatGroupSelected(groupType)) {
// entity type tells us which type of compat group was
// chosen
groupSubtype = new int[] {
AppdefEntityConstants.APPDEF_TYPE_GROUP_COMPAT_PS,
AppdefEntityConstants.APPDEF_TYPE_GROUP_COMPAT_SVC };
} else if (isAdhocGroupSelected(groupType)) {
if (resourceType != DEFAULT_RESOURCE_TYPE) {
// resourceType straight up tells us what groupsubtype was
// chosen
groupSubtype = new int[] { resourceType };
} else {
groupSubtype = new int[] {
AppdefEntityConstants.APPDEF_TYPE_GROUP_ADHOC_PSS,
AppdefEntityConstants.APPDEF_TYPE_GROUP_ADHOC_APP,
AppdefEntityConstants.APPDEF_TYPE_GROUP_ADHOC_GRP };
}
// for findCompatInventory, resourceType always need
// to be this, for whatever reason
resourceType = DEFAULT_RESOURCE_TYPE;
} else if (isDynamicGroupSelected(groupType)) {
groupSubtype = new int[] { AppdefEntityConstants.APPDEF_TYPE_GROUP_DYNAMIC };
} else {
throw new ServletException("Invalid group type: " + groupType);
}
} else {
// Look up groups
Collection<ResourceGroup> groups = appdefBoss
.findAllGroupPojos(sessionId);
if (groups.size() > 0) {
Map<String, String> groupOptions = new LinkedHashMap<String, String>();
for (ResourceGroup group : groups) {
String appdefKey = AppdefEntityID.newGroupID(group.getId())
.getAppdefKey();
groupOptions.put(appdefKey,group.getName());
}
// Set the group options in request
getServletRequest().setAttribute(Constants.AVAIL_RESGRPS_ATTR,
groupOptions);
}
// Lastly, check for group to filter by
if (hubForm.getFg() != null && hubForm.getFg().length() > 0) {
AppdefEntityID geid = new AppdefEntityID(hubForm.getFg());
gid = geid.getId();
}
}
// TODO: Pass groupSubType as int[]
PageList<AppdefResourceValue> resources = appdefBoss.search(sessionId,
entityType,
org.hyperic.util.StringUtil.escapeForRegex(resourceName, true),
aetid, gid, groupSubtype, hubForm.isAny(), hubForm.isOwn(),
hubForm.isUnavail(), pc);
// Generate root breadcrumb url based on the filter criteria
// submitted...
String rootBrowseUrl = BreadcrumbUtil.createRootBrowseURL(
"resourceHub.action", hubForm, pc);
// ...store it in the session, so that the bread crumb tag can get at it
session.setAttribute(Constants.ROOT_BREADCRUMB_URL_ATTR_NAME,
rootBrowseUrl);
watch.markTimeEnd("findCompatInventory");
getServletRequest().setAttribute(Constants.ALL_RESOURCES_ATTR,
resources);
ActionContext.getContext().put(Constants.ALL_RESOURCES_ATTR, resources);
boolean canModify = false;
ArrayList<AppdefEntityID> ids = new ArrayList<AppdefEntityID>();
// ...to determine the resource type, first see if we have any
// resources...
if (resources != null && resources.size() > 0) {
// ...use the first element to get the resource type...
AppdefResourceValue resource = (AppdefResourceValue) resources
.get(0);
try {
AuthzSubject subject = authzBoss.getCurrentSubject(sessionId);
// ...check to see if user can modify resources of this type...
alertPermissionManager.canModifyAlertDefinition(subject,
resource.getEntityId());
canModify = true;
} catch (PermissionException e) {
// ...user doesn't have permission to modify this resource
// type...
log.debug("No permission to modify alert definition for resource: "
+ resource.getEntityId());
}
for (AppdefResourceValue rv : resources) {
ids.add(rv.getEntityId());
}
}
getServletRequest().setAttribute(Constants.CAN_MODIFY_ALERT_ATTR,
canModify);
watch.markTimeBegin("batchGetIndicators");
if (ids.size() > 0) {
if (prefView.equals(ResourceHubFormNG.LIST_VIEW) && !isGroupSelected
&& resourceType != DEFAULT_RESOURCE_TYPE) {
// Get the indicator templates
HashSet<String> cats = new HashSet<String>(3);
cats.add(MeasurementConstants.CAT_UTILIZATION);
cats.add(MeasurementConstants.CAT_THROUGHPUT);
cats.add(MeasurementConstants.CAT_PERFORMANCE);
Collection<MeasurementTemplate> templates = null;
Map<AppdefEntityID, String[]> metricsMap = new HashMap<AppdefEntityID, String[]>(
ids.size());
for (AppdefEntityID entityId : ids) {
if (templates == null || templates.size() == 0) {
templates = measurementBoss.getDesignatedTemplates(
sessionId, entityId, cats);
if (templates.size() > 0) {
getServletRequest().setAttribute("Indicators",
templates);
}
}
String[] metrics = getResourceMetrics(getServletRequest(),
sessionId, measurementBoss, templates, entityId);
metricsMap.put(entityId, metrics);
}
getServletRequest().setAttribute("indicatorsMap", metricsMap);
}
}
watch.markTimeEnd("batchGetIndicators");
// retrieve inventory summary
watch.markTimeBegin("getInventorySummary");
AppdefInventorySummary summary = appdefBoss.getInventorySummary(
sessionId, false);
getServletRequest().setAttribute(Constants.RESOURCE_SUMMARY_ATTR,
summary);
watch.markTimeEnd("getInventorySummary");
watch.markTimeBegin("findAllResourceTypes");
// generate list of selectable resource types for the chosen
// entity type.
pc = PageControl.PAGE_ALL;
if (isGroupSelected) {
if (isCompatGroupSelected(groupType)) {
// the entity is a compatible group- we build a
// combined menu containing all platform, server and
// service types
final Collection<Integer> platformTypeIds = summary
.getPlatformTypeMap().keySet();
final Collection<AppdefResourceTypeValue> platformTypes = new ArrayList<AppdefResourceTypeValue>(
platformTypeIds.size());
for (final Integer id : platformTypeIds) {
platformTypes.add(appdefBoss.findResourceTypeByResId(
sessionId, id));
}
addCompatTypeOptions(hubForm, platformTypes,
msg(getServletRequest(), PLATFORM_KEY));
final Collection<Integer> serverTypeIds = summary
.getServerTypeMap().keySet();
final Collection<AppdefResourceTypeValue> serverTypes = new ArrayList<AppdefResourceTypeValue>(
serverTypeIds.size());
for (final Integer id : serverTypeIds) {
serverTypes.add(appdefBoss.findResourceTypeByResId(
sessionId, id));
}
addCompatTypeOptions(hubForm, serverTypes,
msg(getServletRequest(), SERVER_KEY));
final Collection<Integer> serviceTypeIds = summary
.getServiceTypeMap().keySet();
final Collection<AppdefResourceTypeValue> serviceTypes = new ArrayList<AppdefResourceTypeValue>(
serviceTypeIds.size());
for (final Integer id : serviceTypeIds) {
serviceTypes.add(appdefBoss.findResourceTypeByResId(
sessionId, id));
}
addCompatTypeOptions(hubForm, serviceTypes,
msg(request, SERVICE_KEY));
} else if (isAdhocGroupSelected(groupType)) {
// the entity is an adhoc group- we offer no adhoc group
// options
addMixedTypeOptions(hubForm);
} else if (isDynamicGroupSelected(groupType)) {
addDynamicTypeOptions(hubForm);
} else {
throw new ServletException("invalid group type: " + groupType);
}
} else {
Collection<AppdefResourceTypeValue> types = new TreeSet<AppdefResourceTypeValue>(
getTypeComparator());
if (entityType == AppdefEntityConstants.APPDEF_TYPE_PLATFORM) {
final Collection<Integer> platformTypeIds = summary
.getPlatformTypeMap().keySet();
for (final Integer id : platformTypeIds) {
types.add(appdefBoss.findResourceTypeByResId(sessionId, id));
}
} else if (entityType == AppdefEntityConstants.APPDEF_TYPE_SERVER) {
final Collection<Integer> serverTypeIds = summary
.getServerTypeMap().keySet();
for (final Integer id : serverTypeIds) {
types.add(appdefBoss.findResourceTypeByResId(sessionId, id));
}
} else if (entityType == AppdefEntityConstants.APPDEF_TYPE_SERVICE) {
final Collection<Integer> serviceTypeIds = summary
.getServiceTypeMap().keySet();
for (final Integer id : serviceTypeIds) {
types.add(appdefBoss.findResourceTypeByResId(sessionId, id));
}
}
addTypeOptions(hubForm, types);
}
watch.markTimeEnd("findAllResourceTypes");
if (log.isDebugEnabled()) {
log.debug("ResourceHubPortalAction: " + watch);
}
// Save the preferences if necessary
if (prefChanged) {
authzBoss.setUserPrefs(user.getSessionId(), user.getId(),
user.getPreferences());
}
// clean out the return path
SessionUtils.resetReturnPath(getServletRequest().getSession());
Portal portal = Portal.createPortal("resource.hub.ResourceHubTitle",
".resource.hub");
getServletRequest().setAttribute(Constants.PORTAL_KEY, portal);
getServletRequest().setAttribute(Constants.INVENTORY_HIERARCHY_ATTR,
navHierarchy);
getServletRequest().setAttribute("ignoreBreadcrumb",true);
return SUCCESS;
}
private Comparator<AppdefResourceTypeValue> getTypeComparator() {
return new Comparator<AppdefResourceTypeValue>() {
public int compare(AppdefResourceTypeValue o1,
AppdefResourceTypeValue o2) {
if (o1 == o2) {
return 0;
}
int rtn = o1.getName().toLowerCase()
.compareTo(o2.getName().toLowerCase());
if (rtn != 0) {
return rtn;
} else {
return o1.getId().compareTo(o2.getId());
}
}
};
}
private String[] getResourceMetrics(HttpServletRequest request,
int sessionId, MeasurementBoss mboss,
Collection<MeasurementTemplate> templates,
final AppdefEntityID entityId) throws RemoteException {
Map<Integer, MetricValue> vals = mboss.getLastIndicatorValues(
sessionId, entityId);
// Format the values
String[] metrics = new String[templates.size()];
if (vals.size() == 0) {
Arrays.fill(metrics, msg(request, "common.value.notavail"));
} else {
int i = 0;
for (Iterator<MeasurementTemplate> it = templates.iterator(); it
.hasNext(); i++) {
MeasurementTemplate mt = it.next();
if (vals.containsKey(mt.getId())) {
MetricValue mv = vals.get(mt.getId());
FormattedNumber fn = UnitsConvert.convert(mv.getValue(),
mt.getUnits());
metrics[i] = fn.toString();
} else {
metrics[i] = msg(request, "common.value.notavail");
}
}
}
return metrics;
}
private void addTypeOptions(ResourceHubFormNG form,
Collection<? extends AppdefResourceTypeValue> types) {
for (AppdefResourceTypeValue value : types) {
form.addType(value.getName(), value.getAppdefTypeKey());
}
}
private void addCompatTypeOptions(ResourceHubFormNG form,
Collection<? extends AppdefResourceTypeValue> types, String label) {
if (types != null && !types.isEmpty()) {
form.addType(BLANK_LABEL, BLANK_VAL);
form.addType(label, BLANK_VAL);
addTypeOptions(form, types);
}
}
private void addMixedTypeOptions(ResourceHubFormNG form) {
form.addType(
getText("resource.group.inventory.New.props.GroupOfGroups"),
String.valueOf(AppdefEntityConstants.APPDEF_TYPE_GROUP_ADHOC_GRP));
form.addType(
getText("resource.group.inventory.New.props.GroupOfMixed"),
String.valueOf(AppdefEntityConstants.APPDEF_TYPE_GROUP_ADHOC_PSS));
form.addType(
getText("resource.group.inventory.New.props.GroupOfApplications"),
String.valueOf(AppdefEntityConstants.APPDEF_TYPE_GROUP_ADHOC_APP));
}
private void addDynamicTypeOptions(ResourceHubFormNG form) {
form.addType(
getText("resource.group.inventory.New.props.DynamicGroup"),
String.valueOf(AppdefEntityConstants.APPDEF_TYPE_GROUP_DYNAMIC));
}
private boolean isAdhocGroupSelected(int type) {
return type == SELECTOR_GROUP_ADHOC;
}
private boolean isDynamicGroupSelected(int type) {
return type == SELECTOR_GROUP_DYNAMIC;
}
private boolean isCompatGroupSelected(int type) {
return type == SELECTOR_GROUP_COMPAT;
}
private boolean isGroupSelected(int type) {
return type == AppdefEntityConstants.APPDEF_TYPE_GROUP;
}
private String msg(HttpServletRequest request, String key) {
return getText(key);
}
public ResourceHubFormNG getModel() {
return hubForm;
}
public int getTotalSize() {
return ((PageList<AppdefResourceValue>) ActionContext.getContext().get(
Constants.ALL_RESOURCES_ATTR)).getTotalSize();
}
public Map<Integer, String> getPaggingList() {
return getPaggingList(getTotalSize());
}
}