/* * RHQ Management Platform * Copyright (C) 2005-2010 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 as published by * the Free Software Foundation version 2 of the License. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.coregui.client.inventory.resource.detail; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.rpc.AsyncCallback; import com.smartgwt.client.types.Alignment; import com.smartgwt.client.types.Cursor; import com.smartgwt.client.types.VerticalAlignment; import com.smartgwt.client.widgets.Canvas; import com.smartgwt.client.widgets.HTMLFlow; import com.smartgwt.client.widgets.Img; import com.smartgwt.client.widgets.Label; import com.smartgwt.client.widgets.Window; import com.smartgwt.client.widgets.events.ClickEvent; import com.smartgwt.client.widgets.events.ClickHandler; import com.smartgwt.client.widgets.form.fields.SpacerItem; import org.rhq.core.domain.auth.Subject; import org.rhq.core.domain.criteria.ResourceCriteria; import org.rhq.core.domain.measurement.AvailabilityType; import org.rhq.core.domain.measurement.ResourceAvailability; import org.rhq.core.domain.resource.Resource; import org.rhq.core.domain.resource.ResourceCategory; import org.rhq.core.domain.resource.ResourceError; import org.rhq.core.domain.resource.composite.ResourceComposite; import org.rhq.core.domain.tagging.Tag; 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.ImageManager; import org.rhq.coregui.client.UserSessionManager; import org.rhq.coregui.client.components.tagging.TagEditorView; import org.rhq.coregui.client.components.tagging.TagsChangedCallback; import org.rhq.coregui.client.dashboard.AutoRefreshUtil; import org.rhq.coregui.client.gwt.GWTServiceLookup; import org.rhq.coregui.client.inventory.AutoRefresh; import org.rhq.coregui.client.util.StringUtility; import org.rhq.coregui.client.util.async.Command; import org.rhq.coregui.client.util.async.CountDownLatch; import org.rhq.coregui.client.util.enhanced.EnhancedHLayout; import org.rhq.coregui.client.util.enhanced.EnhancedVLayout; import org.rhq.coregui.client.util.message.Message; /** * @author Greg Hinkle * @author Ian Springer */ public class ResourceTitleBar extends EnhancedVLayout { //represents row of Resource title details[icon,title,show-details,tags,availability,favorites] private EnhancedHLayout top; //represents normally closed region of Resource details[to verbosely describe resource across all tabs] private EnhancedHLayout details; private static final String FAV_ICON = "Favorite_24_Selected.png"; private static final String NOT_FAV_ICON = "Favorite_24.png"; private static final String EXPANDED_ICON = "[SKIN]/ListGrid/row_expanded.png"; private static final String COLLAPSED_ICON = "[SKIN]/ListGrid/row_collapsed.png"; private static final String COLLAPSED_TOOLTIP = MSG.view_portlet_inventory_tooltip_expand(); private static final String EXPANDED_TOOLTIP = MSG.view_portlet_inventory_tooltip_collapse(); private static final String PLUGIN_ERRORS_ICON = "subsystems/inventory/Resource_warn.png"; //private static final String LOADING_ICON = "[SKIN]/loading.gif"; private Img expandCollapseArrow; private Resource resource; private ResourceComposite resourceComposite; private Img badge; private Img favoriteButton; private HTMLFlow title; private Img availabilityImage; private boolean favorite; private EnhancedHLayout detailsForm; private OverviewForm detailsFormSummary; private Img pluginErrors; // private Img loading; private final ResourceTreeView platformTree; private class AvailAndErrorRefresher implements AutoRefresh { private Timer availAndErrorsRefreshTimer; private volatile boolean refreshingAvail; private volatile boolean refreshingErrors; private final int intervalMillis; public AvailAndErrorRefresher(int intervalMillis) { this.intervalMillis = intervalMillis; } @Override public boolean isRefreshing() { return refreshingAvail || refreshingErrors; } @Override public void startRefreshCycle() { availAndErrorsRefreshTimer = AutoRefreshUtil .startRefreshCycle(this, ResourceTitleBar.this, availAndErrorsRefreshTimer, intervalMillis); } @Override public void refresh() { refresh(true, true); } public void refresh(boolean availability, boolean errors) { int cnt = 0; if (availability && !refreshingAvail) { refreshingAvail = true; ++cnt; } if (errors && !refreshingErrors) { refreshingErrors = true; ++cnt; } if (cnt == 0) { return; } CountDownLatch latch = CountDownLatch.create(cnt, new Command() { @Override public void execute() { CoreGUI.showBusy(false); markForRedraw(); //the checks at the start of the refresh() method above ensure that there is at most //1 avail check and 1 error check running at any single time. //we can therefore be sure here that if refreshing* variable is true, it has been set so when //the request that this latch is guarding has been started. if (refreshingAvail) { refreshingAvail = false; } if (refreshingErrors) { refreshingErrors = false; } } }); CoreGUI.showBusy(true); //loading.markForRedraw(); if (refreshingAvail) { refreshAvailability(latch); } if (refreshingErrors) { refreshErrors(latch); } } public void stop() { AutoRefreshUtil.onDestroy(availAndErrorsRefreshTimer); } private void refreshErrors(final CountDownLatch latch) { GWTServiceLookup.getResourceService().findResourceErrors(resourceComposite.getResource().getId(), new AsyncCallback<List<ResourceError>>() { public void onFailure(Throwable caught) { pluginErrors.setVisible(false); if (!UserSessionManager.isLoggedOut()) { CoreGUI.getErrorHandler().handleError( MSG.dataSource_resourceErrors_error_fetchFailure(String.valueOf(resourceComposite.getResource() .getId())), caught); } if (latch != null) { latch.countDown(); } else { markForRedraw(); } } public void onSuccess(List<ResourceError> result) { pluginErrors.setVisible(!result.isEmpty()); if (latch != null) { latch.countDown(); } else { markForRedraw(); } } }); } private void refreshAvailability(final CountDownLatch latch) { final AvailabilityType currentAvail = resource.getCurrentAvailability().getAvailabilityType(); GWTServiceLookup.getResourceService().getLiveResourceAvailability(resource.getId(), new AsyncCallback<ResourceAvailability>() { @Override public void onFailure(Throwable caught) { availabilityImage.setSrc(ImageManager.getAvailabilityLargeIconFromAvailType(currentAvail)); badge.setStyleName("resource-detail-" + currentAvail.getName()); if (!UserSessionManager.isLoggedOut()) { CoreGUI.getErrorHandler().handleError(MSG.view_inventory_resource_loadFailed(String.valueOf(resource.getId())), caught); } if (latch != null) { latch.countDown(); } else { markForRedraw(); } } @Override public void onSuccess(ResourceAvailability result) { availabilityImage.setSrc(ImageManager.getAvailabilityLargeIconFromAvailType(currentAvail)); resource.setCurrentAvailability(result); badge.setStyleName("resource-detail-" + currentAvail.getName()); availabilityImage.markForRedraw(); if (latch != null) { latch.countDown(); } else { markForRedraw(); } if (currentAvail != result.getAvailabilityType()) { platformTree.refreshResource(resource); } } }); } } private AvailAndErrorRefresher availAndErrorRefresher = new AvailAndErrorRefresher(15000); public ResourceTitleBar(ResourceTreeView platformTree) { super(); this.platformTree = platformTree; //define two rows of content top = new EnhancedHLayout(); details = new EnhancedHLayout(); details.setWidth100(); //modify VLayout settings setWidth100(); setHeight(30); setPadding(5); setMembersMargin(5); } public void update() { //clean up old widgets for (Canvas child : getChildren()) { child.destroy(); } this.title = new HTMLFlow(); this.title.setWidth100(); this.availabilityImage = new Img(ImageManager.getAvailabilityLargeIcon(null), 24, 24); this.favoriteButton = new Img(NOT_FAV_ICON, 24, 24); this.favoriteButton.setCursor(Cursor.POINTER); this.favoriteButton.addClickHandler(new ClickHandler() { public void onClick(ClickEvent clickEvent) { Set<Integer> favorites = toggleFavoriteLocally(); UserSessionManager.getUserPreferences().setFavoriteResources(favorites, new UpdateFavoritesCallback()); } }); badge = new Img(ImageManager.getResourceTypeIcon(ResourceCategory.SERVICE), 24,24); badge.setLayoutAlign(VerticalAlignment.CENTER); TagEditorView tagEditorView = new TagEditorView(resource.getTags(), !resourceComposite.getResourcePermission() .isInventory(), new TagsChangedCallback() { public void tagsChanged(final HashSet<Tag> tags) { GWTServiceLookup.getTagService().updateResourceTags(resource.getId(), tags, new AsyncCallback<Void>() { public void onFailure(Throwable caught) { CoreGUI.getErrorHandler().handleError(MSG.view_resource_title_tagUpdateFailed(), caught); } public void onSuccess(Void result) { CoreGUI.getMessageCenter().notify( new Message(MSG.view_titleBar_common_updateTagsSuccessful(resource.getName()), Message.Severity.Info)); // update what is essentially our local cache resource.setTags(tags); } }); } }); loadTags(tagEditorView); //add expand/collapse icon expandCollapseArrow = new Img(COLLAPSED_ICON, 16, 16); expandCollapseArrow.setTooltip(COLLAPSED_TOOLTIP); expandCollapseArrow.setLayoutAlign(VerticalAlignment.CENTER); expandCollapseArrow.addClickHandler(new ClickHandler() { private boolean collapsed = true; @Override public void onClick(ClickEvent event) { collapsed = !collapsed; if (collapsed) { expandCollapseArrow.setSrc(COLLAPSED_ICON); expandCollapseArrow.setTooltip(COLLAPSED_TOOLTIP); details.hide(); } else { expandCollapseArrow.setSrc(EXPANDED_ICON); expandCollapseArrow.setTooltip(EXPANDED_TOOLTIP); details.show(); } ResourceTitleBar.this.markForRedraw(); } }); pluginErrors = new Img(PLUGIN_ERRORS_ICON, 24, 24); pluginErrors.setVisible(false); //define tool tip pluginErrors.setPrompt(MSG.view_resource_title_component_errors_tooltip()); pluginErrors.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { final Window winModal = new Window(); winModal.setShowMinimizeButton(false); winModal.setShowModalMask(true); winModal.setShowCloseButton(true); winModal.setWidth("70%"); winModal.setHeight("70%"); winModal.setIsModal(true); winModal.setShowResizer(true); winModal.setCanDragResize(true); winModal.centerInPage(); winModal.setTitle(MSG.common_title_componentErrors()); Label disposalReminder = new Label(); disposalReminder.setHeight(12); disposalReminder.setPadding(4); disposalReminder.setAlign(Alignment.CENTER); disposalReminder.setValign(VerticalAlignment.CENTER); disposalReminder.setContents(MSG.view_resource_title_component_errors_cleanup()); EnhancedVLayout form = new EnhancedVLayout(); form.setAlign(VerticalAlignment.CENTER); form.setLayoutMargin(10); form.setWidth100(); form.setHeight100(); Resource resource = resourceComposite.getResource(); ResourceErrorsDataSource errors = new ResourceErrorsDataSource(resource.getId()); ResourceErrorsView errorsGrid = new ResourceErrorsView(null, ResourceTitleBar.this); errorsGrid.setDataSource(errors); form.addMember(errorsGrid); winModal.addItem(disposalReminder); winModal.addItem(form); winModal.setPadding(2); winModal.show(); } }); //loading = new Img(LOADING_ICON, 24, 24); // loading.setVisible(false); //loading.setAlign(Alignment.RIGHT); //loading.setStyleName("spinner"); //loading.setStyleDependentName("hidden", true); //top information top.addMember(expandCollapseArrow); top.addMember(badge); top.addMember(title); top.addMember(pluginErrors); //top.addMember(loading); top.addMember(availabilityImage); top.addMember(favoriteButton); top.setStyleName("resourceSummary"); detailsForm = new EnhancedHLayout(); detailsForm.setWidth100(); detailsForm.setHeight(10); detailsForm.setAlign(Alignment.LEFT); detailsFormSummary = new OverviewForm(resourceComposite, this); detailsFormSummary.setWidth100(); detailsFormSummary.setStyleName("resourceSummaryDetails"); detailsFormSummary.setLayoutAlign(Alignment.LEFT); //condense details for display detailsFormSummary.setHeaderEnabled(false); detailsFormSummary.setDisplayCondensed(true); detailsForm.addMember(detailsFormSummary); SpacerItem widthSpace = new SpacerItem(); widthSpace.setWidth(40); // DynamicForm wrappedSpacer = new DynamicForm(); // wrappedSpacer.setFields(widthSpace); // detailsForm.addMember(wrappedSpacer); details.addChild(detailsForm); details.hide(); //order the components addMember(top); //conditionally add tags. Defaults to true, not available in JON builds. if (CoreGUI.isTagsEnabledForUI()) { addMember(tagEditorView); } addMember(details); ResourceTitleBar.this.markForRedraw(); availAndErrorRefresher.startRefreshCycle(); } @Override protected void onDestroy() { availAndErrorRefresher.stop(); super.onDestroy(); } private void loadTags(final TagEditorView tagEditorView) { ResourceCriteria criteria = new ResourceCriteria(); criteria.addFilterId(resource.getId()); criteria.fetchTags(true); GWTServiceLookup.getResourceService().findResourcesByCriteria(criteria, new AsyncCallback<PageList<Resource>>() { public void onFailure(Throwable caught) { CoreGUI.getErrorHandler().handleError(MSG.view_titleBar_common_loadTagsFailure(resource.getName()), caught); } public void onSuccess(PageList<Resource> result) { LinkedHashSet<Tag> tags = new LinkedHashSet<Tag>(); tags.addAll(result.get(0).getTags()); tagEditorView.setTags(tags); } }); } public void setResource(ResourceComposite resourceComposite, boolean isRefresh) { if (this.resourceComposite == null || resourceComposite.getResource().getId() != this.resource.getId() || isRefresh) { this.resource = resourceComposite.getResource(); this.resourceComposite = resourceComposite; update(); displayResourceName(resource.getName()); Set<Integer> favorites = UserSessionManager.getUserPreferences().getFavoriteResources(); this.favorite = favorites.contains(resource.getId()); updateFavoriteButton(); this.availabilityImage.setSrc(ImageManager.getAvailabilityLargeIconFromAvailType(resource .getCurrentAvailability().getAvailabilityType())); badge.setSrc(ImageManager.getResourceIcon(this.resource.getResourceType().getCategory(), getResource().getAvailability())); markForRedraw(); } } public ResourceComposite getResource() { return resourceComposite; } void displayResourceName(String resourceName) { if (!resource.getName().equals(resourceName)) { resource.setName(resourceName); // the name must have been changed by the user via the editable field } this.title.setContents("<span class=\"SectionHeader\">" + StringUtility.escapeHtml(resourceName) + "</span> <span class=\"subtitle\">" + ResourceTypeUtility.displayName(resource.getResourceType()) + "</span>"); this.title.markForRedraw(); } private void updateFavoriteButton() { this.favoriteButton.setSrc(favorite ? FAV_ICON : NOT_FAV_ICON); if (favorite) { this.favoriteButton.setTooltip(MSG.view_titleBar_common_clickToRemoveFav()); } else { this.favoriteButton.setTooltip(MSG.view_titleBar_common_clickToAddFav()); } } private Set<Integer> toggleFavoriteLocally() { this.favorite = !this.favorite; Set<Integer> favorites = UserSessionManager.getUserPreferences().getFavoriteResources(); int resourceId = this.resource.getId(); if (this.favorite) { favorites.add(resourceId); } else { favorites.remove(resourceId); } return favorites; } public void refreshResourceErrors() { availAndErrorRefresher.refresh(false, true); } public void refreshAvailAndResourceErrors() { availAndErrorRefresher.refresh(); } public class UpdateFavoritesCallback implements AsyncCallback<Subject> { public void onSuccess(Subject subject) { String msg = null; if (favorite) { msg = MSG.view_titleBar_common_addedFav(resource.getName()); } else { msg = MSG.view_titleBar_common_removedFav(resource.getName()); } CoreGUI.getMessageCenter().notify(new Message(msg, Message.Severity.Info)); updateFavoriteButton(); } public void onFailure(Throwable throwable) { String msg = null; if (favorite) { msg = MSG.view_titleBar_common_addedFavFailure(resource.getName()); } else { msg = MSG.view_titleBar_common_removedFavFailure(resource.getName()); } CoreGUI.getErrorHandler().handleError(msg, throwable); // Revert back to our original favorite status, since the server update failed. toggleFavoriteLocally(); } } }