/* * RHQ Management Platform * Copyright (C) 2005-2014 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 General Public License, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also 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 and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.rhq.coregui.client.bundle.tree; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import com.google.gwt.i18n.client.NumberFormat; import com.google.gwt.user.client.rpc.AsyncCallback; import com.smartgwt.client.data.DSRequest; import com.smartgwt.client.data.DSResponse; import com.smartgwt.client.data.DataSourceField; import com.smartgwt.client.data.Record; import com.smartgwt.client.data.fields.DataSourceTextField; import com.smartgwt.client.widgets.grid.ListGridRecord; import com.smartgwt.client.widgets.tree.TreeNode; import org.rhq.core.domain.bundle.Bundle; import org.rhq.core.domain.bundle.BundleDeployment; import org.rhq.core.domain.bundle.BundleDestination; import org.rhq.core.domain.bundle.BundleGroup; import org.rhq.core.domain.bundle.BundleVersion; import org.rhq.core.domain.criteria.BundleCriteria; import org.rhq.core.domain.criteria.BundleDeploymentCriteria; import org.rhq.core.domain.criteria.BundleDestinationCriteria; import org.rhq.core.domain.criteria.BundleGroupCriteria; import org.rhq.core.domain.criteria.BundleVersionCriteria; import org.rhq.core.domain.criteria.Criteria; import org.rhq.core.domain.util.PageList; import org.rhq.core.domain.util.PageOrdering; import org.rhq.coregui.client.CoreGUI; import org.rhq.coregui.client.ImageManager; import org.rhq.coregui.client.gwt.GWTServiceLookup; import org.rhq.coregui.client.util.RPCDataSource; import org.rhq.coregui.client.util.StringUtility; /** * @author Greg Hinkle * @author John Mazzitelli */ public class BundleTreeDataSource extends RPCDataSource<Object, Criteria> { // this is the field that will contain data which can be used to sort the tree nodes public static final String FIELD_SORT_VALUE = "sortValue"; public BundleTreeDataSource() { super(); List<DataSourceField> fields = addDataSourceFields(); addFields(fields); } @Override protected List<DataSourceField> addDataSourceFields() { List<DataSourceField> fields = super.addDataSourceFields(); DataSourceField idDataField = new DataSourceTextField("id", MSG.common_title_id()); idDataField.setPrimaryKey(true); fields.add(idDataField); DataSourceTextField nameDataField = new DataSourceTextField("name", MSG.common_title_name()); nameDataField.setCanEdit(false); fields.add(nameDataField); DataSourceTextField sortValueDataField = new DataSourceTextField(FIELD_SORT_VALUE); sortValueDataField.setCanView(false); fields.add(sortValueDataField); DataSourceTextField parentIdField = new DataSourceTextField("parentId", MSG.common_title_id_parent()); parentIdField.setForeignKey("id"); fields.add(parentIdField); return fields; } @Override protected void executeFetch(final DSRequest request, final DSResponse response, final Criteria unused) { final String parentId = request.getCriteria().getAttribute("parentId"); if (parentId == null) { // there is no parent - we are at the root of the tree // get the bundles and build the bundle groups (including unassigned) from that BundleCriteria criteria = new BundleCriteria(); criteria.fetchBundleGroups(true); criteria.clearPaging(); criteria.addSortName(PageOrdering.ASC); GWTServiceLookup.getBundleService().findBundlesByCriteria(criteria, new AsyncCallback<PageList<Bundle>>() { public void onFailure(Throwable caught) { CoreGUI.getErrorHandler().handleError(MSG.view_bundle_tree_loadFailure(), caught); response.setStatus(DSResponse.STATUS_FAILURE); processResponse(request.getRequestId(), response); } public void onSuccess(final PageList<Bundle> result) { BundleGroup unassignedBundleGroup = new BundleGroup(); unassignedBundleGroup.setId(0); // ID=0 is an indicator we use to denote the unassigned group unassignedBundleGroup.setName(MSG.view_bundle_tree_unassigned_name()); unassignedBundleGroup.setDescription(MSG.view_bundle_tree_unassigned_desc()); // Because findBundleGroupsByCriteria would not have given us all unassigned bundles, we used // findBundlesByCriteria. But we need to organize our tree structure with groups at the top, so // we need to put our results aggregated in groups. HashMap<Integer, BundleGroup> visibleBundleGroups = new HashMap<Integer, BundleGroup>(); for (Bundle bundle : result) { Set<BundleGroup> bundleBundleGroups = bundle.getBundleGroups(); if (bundleBundleGroups == null || bundleBundleGroups.isEmpty()) { unassignedBundleGroup.addBundle(bundle); } else { for (BundleGroup bundleBundleGroup : bundleBundleGroups) { BundleGroup theGroup = visibleBundleGroups.get(bundleBundleGroup.getId()); if (theGroup == null) { visibleBundleGroups.put(bundleBundleGroup.getId(), bundleBundleGroup); theGroup = bundleBundleGroup; } theGroup.addBundle(bundle); } } } final ArrayList<BundleGroup> allVisibleBundleGroups = new ArrayList<BundleGroup>(visibleBundleGroups.values()); if (!unassignedBundleGroup.getBundles().isEmpty()) { allVisibleBundleGroups.add(unassignedBundleGroup); } BundleGroupCriteria bundleGroupCriteria = new BundleGroupCriteria(); bundleGroupCriteria.addFilterIds(visibleBundleGroups.keySet().toArray(new Integer[0])); GWTServiceLookup.getBundleService().findBundleGroupsByCriteria(bundleGroupCriteria, new AsyncCallback<PageList<BundleGroup>>() { public void onFailure(Throwable caught) { // just log a message, but keep going, this just means we can't show lock icons where applicable CoreGUI.getErrorHandler().handleError(MSG.view_bundle_tree_loadFailure(), caught); } public void onSuccess(PageList<BundleGroup> result) { // if any of the bundle group tree nodes represent bundle groups the user isn't allowed // to see, then mark the node with a locked icon HashSet<Integer> permittedBundleGroups = new HashSet<Integer>(); for (BundleGroup bg : result) { permittedBundleGroups.add(bg.getId()); } ListGridRecord[] dataRecords = buildRecords(allVisibleBundleGroups); for (ListGridRecord dataRecord : dataRecords) { // we only want to examine bundle group records - and they are the only ones // with ID attributes that are a simple number without "_" character. // Ignore the Unassigned Bundle Group (whose id = "0") - never show a lock for that. TreeNode dataRecordNode = (TreeNode) dataRecord; String idString = dataRecordNode.getAttribute("id"); if (!idString.contains("_") && !idString.equals("0") && !permittedBundleGroups.contains(Integer.valueOf(idString))) { dataRecordNode.setIcon(ImageManager.getLockedIcon()); dataRecordNode.setEnabled(false); } } response.setData(dataRecords); response.setTotalRows(allVisibleBundleGroups.size()); processResponse(request.getRequestId(), response); } }); } }); } else { String[] splitParentId = parentId.split("_", 3); // <bundleGroupId>_<bundleId>_<and the rest> final Integer bundleGroupId = Integer.parseInt(splitParentId[0]); Integer tmp; try { tmp = Integer.parseInt(splitParentId[1]); } catch (NumberFormatException e) { tmp = null; } final Integer bundleId = tmp; // we are at an inner node, being asked to get the children of it if (parentId.endsWith("_versions")) { BundleVersionCriteria criteria = new BundleVersionCriteria(); criteria.addFilterBundleId(bundleId); GWTServiceLookup.getBundleService().findBundleVersionsByCriteria(criteria, new AsyncCallback<PageList<BundleVersion>>() { public void onFailure(Throwable caught) { CoreGUI.getErrorHandler().handleError(MSG.view_bundle_tree_loadFailure(), caught); response.setStatus(DSResponse.STATUS_FAILURE); processResponse(request.getRequestId(), response); } public void onSuccess(PageList<BundleVersion> result) { response.setData(buildRecordsForKnownBundle(result, bundleGroupId, bundleId)); response.setTotalRows(result.getTotalSize()); processResponse(request.getRequestId(), response); } }); } else if (parentId.endsWith("_deployments")) { BundleDeploymentCriteria criteria = new BundleDeploymentCriteria(); criteria.fetchBundleVersion(true); criteria.addFilterBundleId(bundleId); GWTServiceLookup.getBundleService().findBundleDeploymentsByCriteria(criteria, new AsyncCallback<PageList<BundleDeployment>>() { public void onFailure(Throwable caught) { CoreGUI.getErrorHandler().handleError(MSG.view_bundle_tree_loadFailure(), caught); } public void onSuccess(PageList<BundleDeployment> result) { response.setData(buildRecordsForKnownBundle(result, bundleGroupId, bundleId)); processResponse(request.getRequestId(), response); } }); } else if (parentId.endsWith("_destinations")) { BundleDestinationCriteria criteria = new BundleDestinationCriteria(); criteria.addFilterBundleId(bundleId); criteria.fetchDeployments(true); GWTServiceLookup.getBundleService().findBundleDestinationsByCriteria(criteria, new AsyncCallback<PageList<BundleDestination>>() { public void onFailure(Throwable caught) { CoreGUI.getErrorHandler().handleError(MSG.view_bundle_tree_loadFailure(), caught); } public void onSuccess(PageList<BundleDestination> result) { response.setData(buildRecordsForKnownBundle(result, bundleGroupId, bundleId)); processResponse(request.getRequestId(), response); } }); } else { // we are at a child node under a bundle node - its an individual destination or deployment node BundleCriteria criteria = new BundleCriteria(); criteria.addFilterId(bundleId); criteria.fetchDestinations(true); GWTServiceLookup.getBundleService().findBundlesByCriteria(criteria, new AsyncCallback<PageList<Bundle>>() { public void onFailure(Throwable caught) { CoreGUI.getErrorHandler().handleError(MSG.view_bundle_tree_loadFailure(), caught); response.setStatus(DSResponse.STATUS_FAILURE); processResponse(request.getRequestId(), response); } public void onSuccess(PageList<Bundle> result) { final ListGridRecord[] listGridRecordsData = buildRecordsForKnownBundle(result, bundleGroupId, bundleId); response.setData(listGridRecordsData); processResponse(request.getRequestId(), response); } }); } } } @Override protected Criteria getFetchCriteria(DSRequest request) { // our executeFetch will determine on its own what criteria to use based on what is to be fetched // thus we don't return anything here and let the executeFetch do everything return null; } @Override public Object copyValues(Record from) { return null; // don't need this method. } @Override public ListGridRecord[] buildRecords(Collection dataObjects) { return buildRecordsForKnownBundle(dataObjects, null, null); } private ListGridRecord[] buildRecordsForKnownBundle(Collection dataObjects, Integer bundleGroupId, Integer bundleId) { if (dataObjects == null) { return null; } final List<ListGridRecord> records = new ArrayList<ListGridRecord>(); for (Object item : dataObjects) { // the resultant item is a direct node to build records.add(copyValuesForKnownBundle(item, bundleGroupId, bundleId)); // now build the children of the node if (item instanceof BundleGroup) { BundleGroup bundleGroup = (BundleGroup) item; Set<Bundle> bundles = bundleGroup.getBundles(); if (bundles != null) { for (Bundle bundle : bundles) { records.add(copyValuesForKnownBundle(bundle, bundleGroup.getId(), bundle.getId())); // while we are here, automatically add the two direct children - the versions and destinations folders TreeNode versionNode = new TreeNode(MSG.view_bundle_versions()); versionNode.setID(bundleGroup.getId() + "_" + bundle.getId() + "_versions"); versionNode.setParentID(bundleGroup.getId() + "_" + bundle.getId()); versionNode.setName(MSG.view_bundle_versions()); records.add(versionNode); TreeNode deploymentsNode = new TreeNode(MSG.view_bundle_destinations()); deploymentsNode.setID(bundleGroup.getId() + "_" + bundle.getId() + "_destinations"); deploymentsNode.setParentID(bundleGroup.getId() + "_" + bundle.getId()); deploymentsNode.setName(MSG.view_bundle_destinations()); records.add(deploymentsNode); } } } else if (item instanceof Bundle) { Bundle bundle = (Bundle) item; // each bundle has two direct children - the versions and destinations folders TreeNode versionNode = new TreeNode(MSG.view_bundle_versions()); versionNode.setID(bundleGroupId.toString() + "_" + bundle.getId() + "_versions"); versionNode.setParentID(bundleGroupId.toString() + "_" + bundle.getId()); versionNode.setName(MSG.view_bundle_versions()); records.add(versionNode); TreeNode deploymentsNode = new TreeNode(MSG.view_bundle_destinations()); deploymentsNode.setID(bundleGroupId.toString() + "_" + bundle.getId() + "_destinations"); deploymentsNode.setParentID(bundleGroupId.toString() + "_" + bundle.getId()); deploymentsNode.setName(MSG.view_bundle_destinations()); records.add(deploymentsNode); } else if (item instanceof BundleDestination) { BundleDestination dest = (BundleDestination) item; // each destination has 0, 1 or more deployments if (dest.getDeployments() != null) { for (BundleDeployment deploy : dest.getDeployments()) { records.add(copyValuesForKnownBundle(deploy, bundleGroupId, bundleId)); } } } } return records.toArray(new ListGridRecord[records.size()]); } @Override public ListGridRecord copyValues(Object from) { return copyValuesForKnownBundle(from, null, null); } private ListGridRecord copyValuesForKnownBundle(Object from, Integer bundleGroupId, Integer bundleId) { String parentID; TreeNode node = new TreeNode(); String sortValue = ""; if (from instanceof BundleGroup) { BundleGroup bundleGroup = (BundleGroup) from; node.setIsFolder(true); node.setIcon("subsystems/bundle/BundleGroup_16.png"); node.setID(String.valueOf(bundleGroup.getId())); node.setName(StringUtility.escapeHtml(bundleGroup.getName())); if (bundleGroup.getId() == 0) { node.setEnabled(false); sortValue = "\uFFFDZZZZ"; // always show this as the last folder node in the tree } else{ sortValue = bundleGroup.getName(); } } else if (from instanceof Bundle) { Bundle bundle = (Bundle) from; node.setIsFolder(true); node.setIcon("subsystems/bundle/Bundle_16.png"); node.setID(String.valueOf(bundleGroupId) + "_" + bundle.getId()); node.setParentID(String.valueOf(bundleGroupId)); node.setName(StringUtility.escapeHtml(bundle.getName())); sortValue = bundle.getName(); } else if (from instanceof BundleVersion) { BundleVersion version = (BundleVersion) from; node.setIsFolder(false); node.setIcon("subsystems/bundle/BundleVersion_16.png"); parentID = bundleGroupId.toString() + "_" + version.getBundle().getId() + "_versions"; node.setParentID(parentID); node.setID(parentID + '_' + version.getId()); node.setName(version.getVersion()); sortValue = NumberFormat.getFormat("000000").format(version.getVersionOrder()); } else if (from instanceof BundleDeployment) { BundleDeployment deployment = (BundleDeployment) from; node.setIsFolder(false); node.setIcon("subsystems/bundle/BundleDeployment_16.png"); parentID = bundleGroupId.toString() + "_" + bundleId + "_destinations_" + deployment.getDestination().getId(); node.setParentID(parentID); node.setID(bundleGroupId.toString() + "_" + bundleId + "_deployments_" + deployment.getId()); String name = StringUtility.escapeHtml(deployment.getName()); if (deployment.isLive()) { node.setName("<span style=\"color: green; font-weight: bold\">(live)</span> " + name); } else { node.setName(name); } sortValue = deployment.getName(); } else if (from instanceof BundleDestination) { BundleDestination destination = (BundleDestination) from; node.setIsFolder(true); node.setIcon("subsystems/bundle/BundleDestination_16.png"); parentID = bundleGroupId.toString() + "_" + destination.getBundle().getId() + "_destinations"; node.setParentID(parentID); node.setID(parentID + '_' + destination.getId()); node.setName(StringUtility.escapeHtml(destination.getName())); sortValue = destination.getName(); } node.setAttribute(FIELD_SORT_VALUE, sortValue.toLowerCase()); return node; } }