/*
* 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.inventory.common.event;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.rpc.RPCResponse;
import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.grid.CellFormatter;
import com.smartgwt.client.widgets.grid.HoverCustomizer;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import org.rhq.core.domain.common.EntityContext;
import org.rhq.core.domain.criteria.EventCriteria;
import org.rhq.core.domain.event.EventSeverity;
import org.rhq.core.domain.event.composite.EventComposite;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.domain.util.PageControl;
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.LinkManager;
import org.rhq.coregui.client.components.table.TimestampCellFormatter;
import org.rhq.coregui.client.gwt.GWTServiceLookup;
import org.rhq.coregui.client.inventory.resource.AncestryUtil;
import org.rhq.coregui.client.inventory.resource.type.ResourceTypeRepository;
import org.rhq.coregui.client.inventory.resource.type.ResourceTypeRepository.TypesLoadedCallback;
import org.rhq.coregui.client.util.RPCDataSource;
/**
* @author Joseph Marques
*/
public class EventCompositeDatasource extends RPCDataSource<EventComposite, EventCriteria> {
public static final String FILTER_SEVERITIES = "severities";
private EntityContext entityContext;
public EventCompositeDatasource(EntityContext context) {
super();
this.entityContext = context;
List<DataSourceField> fields = addDataSourceFields();
addFields(fields);
}
public ArrayList<ListGridField> getListGridFields() {
return getListGridFields(true);
}
/**
* The view that contains the list grid which will display this datasource's data will call this
* method to get the field information which is used to control the display of the data.
*
* @return list grid fields used to display the datasource data
*/
public ArrayList<ListGridField> getListGridFields(boolean showResourceAncestry) {
ArrayList<ListGridField> fields = new ArrayList<ListGridField>(6);
ListGridField timestampField = new ListGridField("timestamp", MSG.view_inventory_eventHistory_timestamp());
TimestampCellFormatter.prepareDateField(timestampField);
fields.add(timestampField);
ListGridField severityField = new ListGridField("severity", MSG.view_inventory_eventHistory_severity());
severityField.setAlign(Alignment.CENTER);
severityField.setCellFormatter(new CellFormatter() {
public String format(Object o, ListGridRecord listGridRecord, int i, int i1) {
String icon = ImageManager.getEventSeverityBadge(EventSeverity.valueOf(o.toString()));
return Canvas.imgHTML(icon, 24, 24);
}
});
severityField.setShowHover(true);
severityField.setHoverCustomizer(new HoverCustomizer() {
public String hoverHTML(Object value, ListGridRecord record, int rowNum, int colNum) {
String sevEnumName = record.getAttribute("severity");
if (sevEnumName == null) {
return null;
}
EventSeverity severity = EventSeverity.valueOf(sevEnumName);
switch (severity) {
case DEBUG:
return MSG.common_severity_debug();
case INFO:
return MSG.common_severity_info();
case WARN:
return MSG.common_severity_warn();
case ERROR:
return MSG.common_severity_error();
case FATAL:
return MSG.common_severity_fatal();
default:
return null;
}
}
});
fields.add(severityField);
ListGridField detailField = new ListGridField("detail", MSG.view_inventory_eventHistory_details());
detailField.setCellFormatter(new CellFormatter() {
@Override
public String format(Object value, ListGridRecord record, int rowNum, int colNum) {
if (null == value) {
return "";
} else if (((String) value).length() <= 200) {
return (String) value;
} else {
return ((String) value).substring(0, 200); // first 200 chars
}
}
});
fields.add(detailField);
ListGridField sourceField = new ListGridField("source", MSG.view_inventory_eventHistory_sourceLocation());
sourceField.setCellFormatter(new CellFormatter() {
public String format(Object o, ListGridRecord listGridRecord, int i, int i1) {
String sourceLocation = listGridRecord.getAttribute("source");
int length = sourceLocation.length();
if (length > 40) {
return "..." + sourceLocation.substring(length - 40); // the last 40 chars
}
return sourceLocation;
}
});
sourceField.setShowHover(true);
sourceField.setHoverCustomizer(new HoverCustomizer() {
public String hoverHTML(Object value, ListGridRecord record, int rowNum, int colNum) {
if (value == null) {
return null;
}
String sourceLocation = (String) value;
return (sourceLocation.length() > 40) ? sourceLocation : null;
}
});
fields.add(sourceField);
if (this.entityContext.type != EntityContext.Type.Resource) {
ListGridField resourceNameField = new ListGridField(AncestryUtil.RESOURCE_NAME, MSG.common_title_resource());
resourceNameField.setCellFormatter(new CellFormatter() {
public String format(Object o, ListGridRecord listGridRecord, int i, int i1) {
String url = LinkManager.getResourceLink(listGridRecord.getAttributeAsInt(AncestryUtil.RESOURCE_ID));
return LinkManager.getHref(url, o.toString());
}
});
resourceNameField.setShowHover(true);
resourceNameField.setHoverCustomizer(new HoverCustomizer() {
public String hoverHTML(Object value, ListGridRecord listGridRecord, int rowNum, int colNum) {
return AncestryUtil.getResourceHoverHTML(listGridRecord, 0);
}
});
fields.add(resourceNameField);
if (showResourceAncestry) {
ListGridField ancestryField = AncestryUtil.setupAncestryListGridField();
fields.add(ancestryField);
ancestryField.setWidth("25%");
}
timestampField.setWidth(155);
severityField.setWidth(55);
detailField.setWidth("*");
sourceField.setWidth(180);
resourceNameField.setWidth("20%");
} else {
timestampField.setWidth(155);
severityField.setWidth(55);
detailField.setWidth("*");
sourceField.setWidth(220);
}
return fields;
}
@Override
public EventComposite copyValues(Record from) {
return null; // TODO: Implement this method.
}
@Override
public ListGridRecord copyValues(EventComposite from) {
ListGridRecord record = new ListGridRecord();
record.setAttribute("id", from.getEventId());
record.setAttribute("timestamp", from.getTimestamp());
record.setAttribute("detail", from.getEventDetail());
record.setAttribute("severity", from.getSeverity().name());
record.setAttribute("source", from.getSourceLocation());
// for ancestry handling
record.setAttribute(AncestryUtil.RESOURCE_ID, from.getResourceId());
record.setAttribute(AncestryUtil.RESOURCE_NAME, from.getResourceName());
record.setAttribute(AncestryUtil.RESOURCE_ANCESTRY, from.getResourceAncestry());
record.setAttribute(AncestryUtil.RESOURCE_TYPE_ID, from.getResourceTypeId());
return record;
}
@Override
protected void executeFetch(final DSRequest request, final DSResponse response, final EventCriteria criteria) {
if (criteria == null) {
// the user selected no severities in the filter - it makes sense from the UI perspective to show 0 rows
response.setTotalRows(0);
processResponse(request.getRequestId(), response);
return;
}
GWTServiceLookup.getEventService().findEventCompositesByCriteria(criteria,
new AsyncCallback<PageList<EventComposite>>() {
public void onFailure(Throwable caught) {
CoreGUI.getErrorHandler().handleError(MSG.view_inventory_eventDetails_loadFailed(), caught);
response.setStatus(RPCResponse.STATUS_FAILURE);
}
public void onSuccess(final PageList<EventComposite> result) {
dataRetrieved(result, response, request);
}
});
}
/**
* Additional processing to support entity-specific or cross-resource views, and something that can be overridden.
*/
protected void dataRetrieved(final PageList<EventComposite> result, final DSResponse response,
final DSRequest request) {
switch (entityContext.type) {
// no need to disambiguate, the alerts are for a singe resource
case Resource:
Record[] records = buildRecords(result);
highlightFilterMatches(request, records);
response.setData(records);
setPagingInfo(response, result);
processResponse(request.getRequestId(), response);
break;
// disambiguate as the results could be cross-resource
default:
Set<Integer> typesSet = new HashSet<Integer>();
Set<String> ancestries = new HashSet<String>();
for (EventComposite composite : result) {
typesSet.add(composite.getResourceTypeId());
ancestries.add(composite.getResourceAncestry());
}
// In addition to the types of the result resources, get the types of their ancestry
typesSet.addAll(AncestryUtil.getAncestryTypeIds(ancestries));
ResourceTypeRepository typeRepo = ResourceTypeRepository.Cache.getInstance();
typeRepo.getResourceTypes(typesSet.toArray(new Integer[typesSet.size()]), new TypesLoadedCallback() {
@Override
public void onTypesLoaded(Map<Integer, ResourceType> types) {
// Smartgwt has issues storing a Map as a ListGridRecord attribute. Wrap it in a pojo.
AncestryUtil.MapWrapper typesWrapper = new AncestryUtil.MapWrapper(types);
Record[] records = buildRecords(result);
highlightFilterMatches(request, records);
for (Record record : records) {
// To avoid a lot of unnecessary String construction, be lazy about building ancestry hover text.
// Store the types map off the records so we can build a detailed hover string as needed.
record.setAttribute(AncestryUtil.RESOURCE_ANCESTRY_TYPES, typesWrapper);
// Build the decoded ancestry Strings now for display
record.setAttribute(AncestryUtil.RESOURCE_ANCESTRY_VALUE, AncestryUtil.getAncestryValue(record));
}
response.setData(records);
setPagingInfo(response, result);
processResponse(request.getRequestId(), response);
}
});
}
}
@SuppressWarnings("unchecked")
@Override
protected EventCriteria getFetchCriteria(final DSRequest request) {
EventSeverity[] severities = getArrayFilter(request, FILTER_SEVERITIES, EventSeverity.class);
if (severities == null || severities.length == 0) {
return null; // user didn't select any severities - return null to indicate no data should be displayed
}
EventCriteria criteria = new EventCriteria();
// This code is unlikely to be necessary as the encompassing view should be using an initial
// sort specifier. But just in case, make sure we set the initial sort. Note that we have to
// manipulate the PageControl directly as per the restrictions on getFetchCriteria() (see jdoc).
PageControl pageControl = getPageControl(request);
if (pageControl.getOrderingFields().isEmpty()) {
pageControl.initDefaultOrderingField("timestamp", PageOrdering.DESC);
}
// TODO: This call is broken in 2.2, http://code.google.com/p/smartgwt/issues/detail?id=490
// when using AdvancedCriteria
Map<String, Object> criteriaMap = request.getCriteria().getValues();
criteria.addFilterSourceName((String) criteriaMap.get("source"));
criteria.addFilterDetail((String) criteriaMap.get("detail"));
if (criteriaMap.get("startTime")!=null) {
Date tmp = (Date) criteriaMap.get("startTime");
criteria.addFilterStartTime(tmp.getTime());
}
if (criteriaMap.get("endTime")!=null) {
Date tmp = (Date) criteriaMap.get("endTime");
criteria.addFilterEndTime(tmp.getTime());
}
// There's no need to add a severities filter to the criteria if the user specified all severities.
if (severities.length != EventSeverity.values().length) {
criteria.addFilterSeverities(severities);
}
criteria.addFilterEntityContext(entityContext);
return criteria;
}
/**
* Sub-classes can override this to add fine-grained control over the result set size. By default the
* total rows are set to the total result set for the query, allowing proper paging. But some views (portlets)
* may want to limit results to a small set (like most recent).
* @param result
* @param response
* @param request
*
* @return should not exceed result.getTotalSize().
*/
protected int getTotalRows(final PageList<EventComposite> result, final DSResponse response, final DSRequest request) {
return result.getTotalSize();
}
protected EntityContext getEntityContext() {
return entityContext;
}
}