/** * 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) [2009-2010], VMware, 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.taglib; import java.io.IOException; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.TagSupport; import org.apache.commons.lang.StringEscapeUtils; import org.hyperic.hq.appdef.shared.AppdefEntityID; import org.hyperic.hq.appdef.shared.AppdefEntityNotFoundException; import org.hyperic.hq.appdef.shared.AppdefEntityTypeID; import org.hyperic.hq.appdef.shared.AppdefGroupValue; import org.hyperic.hq.appdef.shared.AppdefResourceTypeValue; import org.hyperic.hq.appdef.shared.AppdefResourceValue; import org.hyperic.hq.appdef.shared.ApplicationManager; import org.hyperic.hq.auth.shared.SessionNotFoundException; import org.hyperic.hq.auth.shared.SessionTimeoutException; import org.hyperic.hq.authz.server.session.Resource; import org.hyperic.hq.authz.server.session.ResourceGroup; import org.hyperic.hq.authz.shared.PermissionException; import org.hyperic.hq.authz.shared.ResourceGroupManager; import org.hyperic.hq.authz.shared.ResourceManager; import org.hyperic.hq.bizapp.shared.AppdefBoss; import org.hyperic.hq.context.Bootstrap; import org.hyperic.hq.ui.Constants; import org.hyperic.hq.ui.action.resource.hub.BreadcrumbUtil; import org.hyperic.hq.ui.action.resource.hub.ResourceHubFormNG; import org.hyperic.hq.ui.util.RequestUtils; public class ResourceBreadcrumbTag extends TagSupport { private final static long serialVersionUID = 1L; private final static String RESOURCE_BREADCRUMB_TAG_NAME = "breadcrumb"; private final static String RESOURCE_ID_ATTR_NAME = "resourceId"; private final static String CTYPE_ATTR_NAME = "ctype"; private final static String BASE_BROWSE_URL_ATTR_NAME = "baseBrowseUrl"; private final static String BASE_RESOURCE_URL_ATTR_NAME = "baseResourceUrl"; private String resourceId; private String ctype; private String baseBrowseUrl; private String baseResourceUrl; public String getResourceId() { return resourceId; } public void setResourceId(String resourceId) { this.resourceId = resourceId; } public String getCtype() { return ctype; } public void setCtype(String ctype) { this.ctype = ctype; } public String getBaseBrowseUrl() { return baseBrowseUrl; } public void setBaseBrowseUrl(String baseBrowseUrl) { this.baseBrowseUrl = baseBrowseUrl; } public String getBaseResourceUrl() { return baseResourceUrl; } public void setBaseResourceUrl(String baseResourceUrl) { this.baseResourceUrl = baseResourceUrl; } @Override public int doStartTag() throws JspException { try { String resourceId = getResourceId(); String ctype = getCtype(); String baseBrowseUrl = getBaseBrowseUrl(); String baseResourceUrl = getBaseResourceUrl(); // ...first, backup the bread crumb, we may need it later... List<BreadcrumbItem> backupBreadcrumbs = processBackupBreadcrumb(); // ...then, process the bread crumbs... List<BreadcrumbItem> breadcrumbs = processBreadcrumb(resourceId, ctype, baseBrowseUrl, baseResourceUrl); // ...then, render them... renderBreadcrumb(breadcrumbs, backupBreadcrumbs); } catch (Exception e) { throw new JspException(e); } return SKIP_BODY; } private List<BreadcrumbItem> processBackupBreadcrumb() throws ServletException, CloneNotSupportedException { HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); HttpSession session = request.getSession(); String[] returnToValues = request.getParameterValues(Constants.RETURN_TO_LINK_PARAM_NAME); boolean useBackup = false; List<BreadcrumbItem> backup = null; if (returnToValues != null && returnToValues.length > 0) { useBackup = returnToValues[0].equals(Constants.RETURN_TO_LINK_PARAM_VALUE); } if (useBackup) { // ...reset bread crumbs from backup... List<BreadcrumbItem> breadcrumbs = (List<BreadcrumbItem>) session.getAttribute(Constants.BREADCRUMB_SESSION_BACKUP_ATTR_NAME); session.setAttribute(Constants.BREADCRUMB_SESSION_ATTR_NAME, breadcrumbs); } else { List<BreadcrumbItem> breadcrumbs = (List<BreadcrumbItem>) session.getAttribute(Constants.BREADCRUMB_SESSION_ATTR_NAME); if (breadcrumbs != null) { backup = new ArrayList<BreadcrumbItem>(); // ...clone the bread crumbs... for (Iterator<BreadcrumbItem> i = breadcrumbs.iterator(); i.hasNext();) { backup.add((BreadcrumbItem) i.next().clone()); } } } session.setAttribute(Constants.BREADCRUMB_SESSION_BACKUP_ATTR_NAME, backup); return backup; } private List<BreadcrumbItem> processBreadcrumb(String resourceId, String ctype, String baseBrowseUrl, String baseResourceUrl) throws ServletException, SessionNotFoundException, RemoteException, SessionTimeoutException, PermissionException, AppdefEntityNotFoundException { HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); HttpSession session = request.getSession(); ServletContext ctx = pageContext.getServletContext(); AppdefBoss appdefBoss = Bootstrap.getBean(AppdefBoss.class); int sessionId = RequestUtils.getSessionId(request).intValue(); AppdefEntityID appdefEntityId = new AppdefEntityID(resourceId); AppdefResourceValue resource = appdefBoss.findById(sessionId, appdefEntityId); String browseUrl = getBrowseUrl(resource, (String) session.getAttribute(Constants.ROOT_BREADCRUMB_URL_ATTR_NAME)); List<BreadcrumbItem> breadcrumbs = (List<BreadcrumbItem>) session.getAttribute(Constants.BREADCRUMB_SESSION_ATTR_NAME); if (breadcrumbs == null || browseUrl != null) { // ...create the new bread crumb, if we don't already have one or we're coming from the browse page... breadcrumbs = new ArrayList<BreadcrumbItem>(); // ...clear out backup... session.removeAttribute(Constants.BREADCRUMB_SESSION_BACKUP_ATTR_NAME); } // ...create the bread crumb item for this resource... String url = BreadcrumbUtil.createResourceURL(baseResourceUrl, resourceId, ctype); String label = resource.getName(); if (ctype != null && !ctype.equals("")) { // ...we're dealing with an auto group which is a different beast... appdefEntityId = new AppdefEntityTypeID(ctype); AppdefResourceTypeValue resourceType = appdefBoss.findResourceTypeById(sessionId, (AppdefEntityTypeID) appdefEntityId); label = resourceType.getName(); } BreadcrumbItem newCrumb = new BreadcrumbItem(url, resourceId, ctype, label, appdefEntityId); // ...now loop through the bread crumbs and figure out where this crumb fits in // we don't iterate over index 0 bc that'll always the browse crumb... for (int x = breadcrumbs.size() - 1; x > 0; x--) { BreadcrumbItem crumb = breadcrumbs.get(x); if (!newCrumb.equals(crumb) && (isParentOfChild(crumb, newCrumb) || isMemberOfApplication(crumb, newCrumb) || isMemberOfGroup(crumb, newCrumb) || isParentOfAutoGroup(crumb, newCrumb) || isMemberOfAutoGroup(crumb, newCrumb))) { // ...is this a child of the comparison crumb?.. breadcrumbs.add(newCrumb); break; } // ...not a child so clean up the trail... breadcrumbs.remove(x); } if (breadcrumbs.size() < 2) { if (breadcrumbs.size() == 1) { // ...all we have is the browse crumb which may be stale so remove it... breadcrumbs.remove(0); } // ...add the newest browse crumb... breadcrumbs.add(createRootBreadcrumb(resource)); if (newCrumb.isAutoGroup()) { // ...if we're dealing with an auto group, we need to include the parent resource // in the bread crumb... String parentUrl = BreadcrumbUtil.createResourceURL(baseResourceUrl, resourceId, null); breadcrumbs.add(new BreadcrumbItem(parentUrl, resourceId, null, resource.getName(), new AppdefEntityID(resourceId))); } // ...and finally, add the newest bread crumb... breadcrumbs.add(newCrumb); } //...then stash the bread crumbs in the session... session.setAttribute(Constants.BREADCRUMB_SESSION_ATTR_NAME, breadcrumbs); return breadcrumbs; } private void renderBreadcrumb(List<BreadcrumbItem> breadcrumbs, List<BreadcrumbItem> backupBreadcrumbs) throws IOException { HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); StringBuilder markup = new StringBuilder("<ul class=\"breadcrumbs\">"); // Now that the bread crumb has been generated, let's render it... for (int x = 0; x < breadcrumbs.size(); x++) { BreadcrumbItem item = breadcrumbs.get(x); markup.append("<li class=\"item\">"); if (x < (breadcrumbs.size() - 1)) { markup.append("<a href=\"").append(item.getUrl()).append("\">"); markup.append(StringEscapeUtils.escapeHtml(item.getLabel())); markup.append("</a> › "); } else { markup.append(StringEscapeUtils.escapeHtml(item.getLabel())); } markup.append("</li>"); } markup.append("</ul>"); // Stash the browse crumb in a hidden text field for js use markup.append("<input type=\"hidden\" id=\"browseUrlInput\" value=\"").append(breadcrumbs.get(0).getUrl()).append("\" />"); if (backupBreadcrumbs != null && !breadcrumbs.get(0).equals(backupBreadcrumbs.get(0))) { // ...if the browse crumbs don't match, provide a "Return to ..." link. BreadcrumbItem crumb = backupBreadcrumbs.get(backupBreadcrumbs.size() - 1); String returnTo = RequestUtils.message(request, "breadcrumb.returnTo"); markup.append("<span class=\"returnToLink\"><a href=\"").append(BreadcrumbUtil.createReturnToURL(crumb.getUrl())).append("\">"); markup.append(returnTo).append(" ").append(StringEscapeUtils.escapeHtml(crumb.getLabel())); markup.append("</a></span>"); } pageContext.getOut().write(markup.toString()); } private int getGroupType(AppdefGroupValue group) { // ...group type ids used to distinguish between compat and mixed groups... // TODO there are constants that are already in the TypeConstants class but they have the values // swapped for some reason. To be safe, specifying the values here and will look into // refactoring using the TypeConstants class later. final Integer APPDEF_TYPE_GROUP_COMPAT = new Integer(1); final Integer APPDEF_TYPE_GROUP_MIXED = new Integer(2); final Integer APPDEF_TYPE_GROUP_DYNAMIC = new Integer(3); return group.isDynamicGroup() ? APPDEF_TYPE_GROUP_DYNAMIC : (group.isGroupCompat() ? APPDEF_TYPE_GROUP_COMPAT : APPDEF_TYPE_GROUP_MIXED); } private String getBrowseUrl(AppdefResourceValue resource, String url) { String result = url; if (url != null) { //...if we have an url, check to make sure the resource is associated... String test = ResourceHubFormNG.ENTITY_TYPE_ID_PARAM + "=" + resource.getEntityId().getType(); String testGroup = null; if (resource.getEntityId().isGroup()) { // ...if we're dealing with a group, need to do a secondary test... testGroup = ResourceHubFormNG.GROUP_TYPE_ID_PARAM + "=" + getGroupType((AppdefGroupValue) resource); } if ((url.indexOf(test) == -1 && testGroup == null) || (testGroup != null && url.indexOf(test) > -1 && url.indexOf(testGroup) == -1)) { // ...this browse url isn't using the current resource's resource type, // so generate one based off the resource. BreadcrumbItem crumb = createRootBreadcrumb(resource); result = crumb.getUrl(); } } return result; } private BreadcrumbItem createRootBreadcrumb(AppdefResourceValue resource) { HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); HttpSession session = request.getSession(); // ...we have a fresh trail of crumbs to lay down... String browseUrl = (String) session.getAttribute(Constants.ROOT_BREADCRUMB_URL_ATTR_NAME); if (browseUrl == null || browseUrl.equals("")) { // ...we can't find a browse url, so we need to create one from // the resource... Integer groupType = null; if (resource.getEntityId().isGroup()) { // ...we are dealing with a group, let's determine the type (compatible or mixed) groupType = new Integer(getGroupType((AppdefGroupValue) resource)); } browseUrl = BreadcrumbUtil.createRootBrowseURL(baseBrowseUrl, resource.getEntityId().getType(), groupType); } else { // ...we got what we need, remove it from the session... session.removeAttribute(Constants.ROOT_BREADCRUMB_URL_ATTR_NAME); } String browse = RequestUtils.message(request, "breadcrumb.browse"); return new BreadcrumbItem(browseUrl, null, null, browse, null); } private boolean isParentOfChild(BreadcrumbItem parent, BreadcrumbItem child) { boolean result = false; if (!child.isAutoGroup() && ((parent.isPlatform() && child.isServer()) || (parent.isPlatform() && child.isService()) || (parent.isServer() && child.isService()))) { ResourceManager resourceManager =Bootstrap.getBean(ResourceManager.class); Resource parentResource = resourceManager.findResource(parent.getAppdefEntityId()); Resource childResource = resourceManager.findResource(child.getAppdefEntityId()); result = resourceManager.isResourceChildOf(parentResource, childResource); } return result; } private boolean isParentOfAutoGroup(BreadcrumbItem parent, BreadcrumbItem group) { boolean result = false; if ((parent.isPlatform() || parent.isServer() || parent.isApplication()) && group.isAutoGroup()) { ResourceManager resourceManager =Bootstrap.getBean(ResourceManager.class); Resource parentResource = resourceManager.findResource(parent.getAppdefEntityId()); Resource parentOfAutoGroupResource = resourceManager.findResource(new AppdefEntityID(group.getResourceId())); result = parentResource != null && parentResource.equals(parentOfAutoGroupResource); } return result; } private boolean isMemberOfApplication(BreadcrumbItem application, BreadcrumbItem member) { boolean result = false; if (application.isApplication()) { ApplicationManager applicationManager = Bootstrap.getBean(ApplicationManager.class); result = applicationManager.isApplicationMember(application.getAppdefEntityId(), member.getAppdefEntityId()); } return result; } private boolean isMemberOfGroup(BreadcrumbItem group, BreadcrumbItem member) { boolean result = false; if (group.isGroup()) { ResourceManager resourceManager =Bootstrap.getBean(ResourceManager.class); Resource groupResource = resourceManager.findResource(group.getAppdefEntityId()); Resource memberResource = resourceManager.findResource(member.getAppdefEntityId()); ResourceGroupManager resourceGroupManager = Bootstrap.getBean(ResourceGroupManager.class); ResourceGroup resourceGroup = resourceGroupManager.getResourceGroupByResource(groupResource); result = resourceGroupManager.isMember(resourceGroup, memberResource); } return result; } private boolean isMemberOfAutoGroup(BreadcrumbItem group, BreadcrumbItem member) throws SessionNotFoundException, SessionTimeoutException, RemoteException, ServletException { boolean result = false; if (group.isAutoGroup() && !member.isAutoGroup() && (member.isService() || member.isServer())) { HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); ServletContext ctx = pageContext.getServletContext(); AppdefBoss appdefBoss = Bootstrap.getBean(AppdefBoss.class); int sessionId = RequestUtils.getSessionId(request).intValue(); ResourceManager resourceManager =Bootstrap.getBean(ResourceManager.class); AppdefResourceTypeValue autoGroupResourceType = appdefBoss.findResourceTypeById(sessionId, (AppdefEntityTypeID) group.getAppdefEntityId()); Resource memberResource = resourceManager.findResource(member.getAppdefEntityId()); if (autoGroupResourceType.getAppdefType() == memberResource.getResourceType().getAppdefType()) { // ...resource types are the same, now check if parents are the same, // luckily resourceId represents the parent... AppdefEntityID parentAppdef = new AppdefEntityID(group.getResourceId()); if (parentAppdef.isApplication()) { // ...if the parent is an application we need to determine if the other is a member... ApplicationManager applicationManager = Bootstrap.getBean(ApplicationManager.class); result = applicationManager.isApplicationMember(parentAppdef, member.getAppdefEntityId()); } else { // ...otherwise, we check if it's a child resource... Resource parentResource = resourceManager.findResource(parentAppdef); result = resourceManager.isResourceChildOf(parentResource, memberResource); } } } return result; } protected class BreadcrumbItem implements Cloneable { private String url; private String resourceId; private String autoGroupId; private String label; private AppdefEntityID appdefEntityId; public BreadcrumbItem() { } public BreadcrumbItem(String url, String resourceId, String autoGroupId, String label, AppdefEntityID appdefEntityId) { setUrl(url); setResourceId(resourceId); setAutoGroupId(autoGroupId); setLabel(label); setAppdefEntityId(appdefEntityId); } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getResourceId() { return resourceId; } public void setResourceId(String resourceId) { this.resourceId = resourceId; } public String getAutoGroupId() { return autoGroupId; } public void setAutoGroupId(String autoGroupId) { this.autoGroupId = autoGroupId; } public String getLabel() { return label; } public void setLabel(String label) { this.label = label; } public AppdefEntityID getAppdefEntityId() { return appdefEntityId; } public void setAppdefEntityId(AppdefEntityID appdefEntityId) { this.appdefEntityId = appdefEntityId; } public boolean isApplication() { return this.appdefEntityId.isApplication(); } public boolean isAutoGroup() { return this.appdefEntityId instanceof AppdefEntityTypeID; } public boolean isGroup() { return this.appdefEntityId.isGroup(); } public boolean isPlatform() { return this.appdefEntityId.isPlatform(); } public boolean isServer() { return this.appdefEntityId.isServer(); } public boolean isService() { return this.appdefEntityId.isService(); } @Override public boolean equals(Object obj) { BreadcrumbItem crumb = (BreadcrumbItem) obj; return crumb != null && ((crumb.getAutoGroupId() == null && this.getAutoGroupId() == null) || (crumb.getAutoGroupId() != null && crumb.getAutoGroupId().equals(this.getAutoGroupId()))) && ((crumb.getLabel() == null && this.getLabel() == null) || (crumb.getLabel() != null && crumb.getLabel().equals(this.getLabel()))) && ((crumb.getResourceId() == null && this.getResourceId() == null) || (crumb.getResourceId() != null && crumb.getResourceId().equals(this.getResourceId()))) && ((crumb.getUrl() == null && this.getUrl() == null) || (crumb.getUrl() != null && crumb.getUrl().equals(this.getUrl()))) && ((crumb.getAppdefEntityId() == null && this.getAppdefEntityId() == null) || (crumb.getAppdefEntityId() != null && crumb.getAppdefEntityId().equals(this.getAppdefEntityId()))); } @Override protected Object clone() throws CloneNotSupportedException { return new BreadcrumbItem(getUrl(), getResourceId(), getAutoGroupId(), getLabel(), getAppdefEntityId()); } } }