/* * RHQ Management Platform * Copyright (C) 2005-2011 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.inventory.resource; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import com.google.gwt.user.client.rpc.AsyncCallback; import com.smartgwt.client.data.Criteria; import com.smartgwt.client.data.SortSpecifier; import com.smartgwt.client.widgets.grid.ListGridRecord; import org.rhq.core.domain.criteria.ResourceCriteria; import org.rhq.core.domain.resource.CannotConnectToAgentException; import org.rhq.core.domain.resource.DeleteResourceHistory; import org.rhq.core.domain.resource.Resource; import org.rhq.core.domain.resource.ResourceType; import org.rhq.core.domain.resource.composite.ResourceComposite; import org.rhq.core.domain.resource.composite.ResourcePermission; import org.rhq.core.domain.util.PageList; import org.rhq.core.domain.util.ResourceTypeUtility; import org.rhq.coregui.client.CoreGUI; import org.rhq.coregui.client.components.table.AbstractTableAction; import org.rhq.coregui.client.components.table.TableActionEnablement; import org.rhq.coregui.client.gwt.GWTServiceLookup; import org.rhq.coregui.client.gwt.ResourceGWTServiceAsync; import org.rhq.coregui.client.inventory.resource.factory.ResourceFactoryCreateWizard; import org.rhq.coregui.client.inventory.resource.factory.ResourceFactoryImportWizard; import org.rhq.coregui.client.util.Log; import org.rhq.coregui.client.util.RPCDataSource; import org.rhq.coregui.client.util.TableUtility; import org.rhq.coregui.client.util.enhanced.EnhancedIButton.ButtonColor; import org.rhq.coregui.client.util.message.Message; import org.rhq.coregui.client.util.message.Message.Severity; /** * @author Jay Shaughnessy */ public class ResourceCompositeSearchView extends ResourceSearchView { private final ResourceComposite parentResourceComposite; private boolean initialized; private List<Resource> singletonChildren; private Set<ResourceType> creatableChildTypes; private Set<ResourceType> importableChildTypes; private boolean hasCreatableTypes; private boolean hasImportableTypes; private boolean canCreate; public ResourceCompositeSearchView(ResourceComposite parentResourceComposite, Criteria criteria, String title, SortSpecifier[] sortSpecifier, String[] excludeFields, String headerIcon) { super(criteria, title, sortSpecifier, excludeFields, headerIcon); this.parentResourceComposite = parentResourceComposite; this.canCreate = this.parentResourceComposite.getResourcePermission().isCreateChildResources(); setInitialCriteriaFixed(true); } public ResourceCompositeSearchView(ResourceComposite parentResourceComposite, Criteria criteria, String title, String headerIcon) { this(parentResourceComposite, criteria, title, null, null, headerIcon); } @Override protected void onInit() { // To properly filter Create Child and Import menus we need existing singleton child resources. If the // user has create permission and the parent type has singleton child types and creatable or importable child // types, perform an async call to fetch the singleton children. If we make the async call don't declare this // instance initialized until after it completes as we must have the children before the menu buttons can be drawn. final Resource parentResource = parentResourceComposite.getResource(); ResourceType parentType = parentResource.getResourceType(); creatableChildTypes = getCreatableChildTypes(parentType); importableChildTypes = getImportableChildTypes(parentType); hasCreatableTypes = !creatableChildTypes.isEmpty(); hasImportableTypes = !importableChildTypes.isEmpty(); refreshSingletons(parentResource, new AsyncCallback<PageList<Resource>>() { public void onFailure(Throwable caught) { ResourceCompositeSearchView.super.onInit(); initialized = true; } public void onSuccess(PageList<Resource> result) { ResourceCompositeSearchView.super.onInit(); initialized = true; } }); } private void refreshSingletons(final Resource parentResource, final AsyncCallback<PageList<Resource>> callback) { singletonChildren = new ArrayList<Resource>(); // initialize to non-null Integer[] singletonChildTypes = getSingletonChildTypes(parentResource.getResourceType()); if (canCreate && singletonChildTypes.length > 0 && (hasCreatableTypes || hasImportableTypes)) { ResourceCriteria criteria = new ResourceCriteria(); criteria.addFilterParentResourceId(parentResource.getId()); criteria.addFilterResourceTypeIds(singletonChildTypes); GWTServiceLookup.getResourceService().findResourcesByCriteria(criteria, new AsyncCallback<PageList<Resource>>() { @Override public void onSuccess(PageList<Resource> result) { singletonChildren = result; if (callback != null) { callback.onSuccess(result); } } @Override public void onFailure(Throwable caught) { Log.error("Failed to load child resources for [" + parentResource + "]", caught); if (callback != null) { callback.onFailure(caught); } } }); } else { if (callback != null) { callback.onSuccess(new PageList<Resource>()); } } } @Override public boolean isInitialized() { return super.isInitialized() && this.initialized; } // suppress unchecked warnings because the superclass has different generic types for the datasource @SuppressWarnings("rawtypes") @Override protected RPCDataSource getDataSourceInstance() { return ResourceCompositeDataSource.getInstance(); } @Override protected void configureTable() { addTableAction(MSG.common_button_delete(), MSG.view_inventory_resources_deleteConfirm(), ButtonColor.RED, new AbstractTableAction(TableActionEnablement.ANY) { // only enabled if all selected are a deletable type and if the user has delete permission // on the resources. public boolean isEnabled(ListGridRecord[] selection) { boolean isEnabled = super.isEnabled(selection); if (isEnabled) { for (ListGridRecord record : selection) { ResourceComposite resComposite = (ResourceComposite) record .getAttributeAsObject("resourceComposite"); Resource res = resComposite.getResource(); if (!(isEnabled = res.getResourceType().isDeletable())) { break; } ResourcePermission resPermission = resComposite.getResourcePermission(); if (!(isEnabled = resPermission.isDeleteResource())) { break; } } } return isEnabled; } public void executeAction(ListGridRecord[] selection, Object actionValue) { int[] resourceIds = TableUtility.getIds(selection); ResourceGWTServiceAsync resourceManager = GWTServiceLookup.getResourceService(); resourceManager.deleteResources(resourceIds, new AsyncCallback<List<DeleteResourceHistory>>() { public void onFailure(Throwable caught) { if (caught instanceof CannotConnectToAgentException) { CoreGUI.getMessageCenter().notify( new Message(MSG.view_inventory_resources_deleteFailed2(), Severity.Warning)); } else { CoreGUI.getErrorHandler().handleError(MSG.view_inventory_resources_deleteFailed(), caught); } } public void onSuccess(List<DeleteResourceHistory> result) { CoreGUI.getMessageCenter().notify( new Message(MSG.view_inventory_resources_deleteSuccessful(), Severity.Info)); refresh(true); // refresh the entire gui so it encompasses any relevant tree view. Don't just call this.refresh(), // because CoreGUI.refresh is more comprehensive. CoreGUI.refresh(); } }); } }); addImportAndCreateButtons(false); super.configureTable(); } @SuppressWarnings("unchecked") private void addImportAndCreateButtons(boolean override) { final Resource parentResource = parentResourceComposite.getResource(); // Create Child Menu and Manual Import Menu if (canCreate && (hasCreatableTypes || hasImportableTypes)) { if (hasCreatableTypes) { Map<String, ResourceType> displayNameMap = getDisplayNames(creatableChildTypes); LinkedHashMap<String, ResourceType> createTypeValueMap = new LinkedHashMap<String, ResourceType>( displayNameMap); removeExistingSingletons(singletonChildren, createTypeValueMap); AbstractTableAction createAction = new AbstractTableAction(TableActionEnablement.ALWAYS) { public void executeAction(ListGridRecord[] selection, Object actionValue) { ResourceFactoryCreateWizard.showCreateWizard(parentResource, (ResourceType) actionValue); // we can refresh the table buttons immediately since the wizard is a dialog, the // user can't access enabled buttons anyway. ResourceCompositeSearchView.this.refreshTableInfo(); } }; if (override) { updateTableAction(MSG.common_button_create_child(), createTypeValueMap, createAction); } else { addTableAction(MSG.common_button_create_child(), null, createTypeValueMap, ButtonColor.BLUE, createAction); } } if (hasImportableTypes) { Map<String, ResourceType> displayNameMap = getDisplayNames(importableChildTypes); LinkedHashMap<String, ResourceType> importTypeValueMap = new LinkedHashMap<String, ResourceType>( displayNameMap); removeExistingSingletons(singletonChildren, importTypeValueMap); AbstractTableAction importAction = new AbstractTableAction(TableActionEnablement.ALWAYS) { public void executeAction(ListGridRecord[] selection, Object actionValue) { ResourceFactoryImportWizard.showImportWizard(parentResource, (ResourceType) actionValue); // we can refresh the table buttons immediately since the wizard is a dialog, the // user can't access enabled buttons anyway. ResourceCompositeSearchView.this.refreshTableInfo(); } }; if (override) { updateTableAction(MSG.common_button_import(), importTypeValueMap, importAction); } else { addTableAction(MSG.common_button_import(), null, importTypeValueMap, ButtonColor.BLUE, importAction); } } } else if (!override) { if (!canCreate && hasCreatableTypes) { addTableAction(MSG.common_button_create_child(), ButtonColor.BLUE, new AbstractTableAction( TableActionEnablement.NEVER) { public void executeAction(ListGridRecord[] selection, Object actionValue) { // never called } }); } if (!canCreate && hasImportableTypes) { addTableAction(MSG.common_button_import(), ButtonColor.BLUE, new AbstractTableAction( TableActionEnablement.NEVER) { public void executeAction(ListGridRecord[] selection, Object actionValue) { // never called } }); } } } private void removeExistingSingletons(List<Resource> singletonChildren, Map<String, ResourceType> displayNameMap) { List<String> existingSingletons = new ArrayList<String>(); Set<String> displayNames = displayNameMap.keySet(); for (final String displayName : displayNames) { final ResourceType type = displayNameMap.get(displayName); boolean exists = false; if (type.isSingleton()) { for (Resource child : singletonChildren) { exists = child.getResourceType().equals(displayNameMap.get(displayName)); if (exists) { existingSingletons.add(displayName); break; } } } } for (String existing : existingSingletons) { displayNameMap.remove(existing); } } private static Integer[] getSingletonChildTypes(ResourceType type) { Set<Integer> results = new TreeSet<Integer>(); Set<ResourceType> childTypes = type.getChildResourceTypes(); for (ResourceType childType : childTypes) { if (childType.isSingleton()) { results.add(childType.getId()); } } return results.toArray(new Integer[results.size()]); } private static Set<ResourceType> getImportableChildTypes(ResourceType type) { Set<ResourceType> results = new TreeSet<ResourceType>(); Set<ResourceType> childTypes = type.getChildResourceTypes(); for (ResourceType childType : childTypes) { if (childType.isSupportsManualAdd()) { results.add(childType); } } return results; } private static Set<ResourceType> getCreatableChildTypes(ResourceType type) { Set<ResourceType> results = new TreeSet<ResourceType>(); Set<ResourceType> childTypes = type.getChildResourceTypes(); for (ResourceType childType : childTypes) { if (childType.isCreatable()) { results.add(childType); } } return results; } private static Map<String, ResourceType> getDisplayNames(Set<ResourceType> types) { Set<String> allNames = new HashSet<String>(); Set<String> repeatedNames = new HashSet<String>(); for (ResourceType type : types) { String typeName = type.getName(); if (allNames.contains(typeName)) { repeatedNames.add(typeName); } else { allNames.add(typeName); } } Map<String, ResourceType> results = new TreeMap<String, ResourceType>(); for (ResourceType type : types) { String displayName = ResourceTypeUtility.displayName(type); if (repeatedNames.contains(type.getName())) { displayName += " (" + type.getPlugin() + " plugin)"; } results.put(displayName, type); } return results; } protected void onUninventorySuccess() { refresh(true); // refresh the entire gui so it encompasses any relevant tree view. Don't just call this.refresh(), // because CoreGUI.refresh is more comprehensive. CoreGUI.refresh(); } public ResourceComposite getParentResourceComposite() { return parentResourceComposite; } // -------- Static Utility loaders ------------ public static ResourceCompositeSearchView getChildrenOf(ResourceComposite parentResourceComposite) { return new ResourceCompositeSearchView(parentResourceComposite, new Criteria("parentId", String.valueOf(parentResourceComposite.getResource().getId())), MSG.view_tabs_common_child_resources(), null); } @Override public void refresh() { refreshSingletons(parentResourceComposite.getResource(), new AsyncCallback<PageList<Resource>>() { @Override public void onSuccess(PageList<Resource> result) { addImportAndCreateButtons(true); ResourceCompositeSearchView.super.refresh(); } @Override public void onFailure(Throwable caught) { ResourceCompositeSearchView.super.refresh(); } }); } }