/*
* 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 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.operation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
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.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import org.rhq.core.domain.criteria.Criteria;
import org.rhq.core.domain.operation.composite.GroupOperationScheduleComposite;
import org.rhq.core.domain.operation.composite.OperationScheduleComposite;
import org.rhq.core.domain.operation.composite.ResourceOperationScheduleComposite;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.domain.util.PageControl;
import org.rhq.core.domain.util.PageList;
import org.rhq.coregui.client.CoreGUI;
import org.rhq.coregui.client.dashboard.Portlet;
import org.rhq.coregui.client.dashboard.portlets.recent.operations.OperationSchedulePortlet;
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;
// Note, there is nothing in this class that requires ResourceOperationScheduleComposite, it can all be driven
// off ResourceOperationSchedule. We should improve te scheduled op datamodel/api at some point and the
// composite could probably go away.
/**
* Responsible for defining and populating the Smart GWT datasource details and
* translating the deserialized content into specific record entries for display.
*
* @author Simeon Pinder
* @author Jay Shaughnessy
*/
public class ScheduledOperationsDataSource extends RPCDataSource<OperationScheduleComposite, Criteria> {
public enum Field {
OPERATION("operationName", MSG.dataSource_operationSchedule_field_operationName()),
// the key has to remain 'resource' for AncestryUtil.getResourceHoverHTML()
RESOURCE_OR_GROUP("resource", MSG.common_title_resource() + " / " + MSG.common_title_group()),
TIME("operationNextFireTime", MSG.dataSource_operationSchedule_field_nextFireTime()),
GROUP_ID("groupId", "groupId"),
GROUP_TYPE("groupType", MSG.common_title_resource_name()),
IS_AUTO_GROUP("isAutoGroup", "isAutoGroup");
/**
* Corresponds to a property name of Resource (e.g. resourceType.name).
*/
private String propertyName;
/**
* The table header for the field or property (e.g. Type).
*/
private String title;
private Field(String propertyName, String title) {
this.propertyName = propertyName;
this.title = title;
}
public String propertyName() {
return propertyName;
}
public String title() {
return title;
}
public ListGridField getListGridField() {
return new ListGridField(propertyName, title);
}
public ListGridField getListGridField(int width) {
return new ListGridField(propertyName, title, width);
}
}
private Portlet portlet;
//config settings
private boolean operationsRangeNextEnabled = false;
private int operationsRangeScheduled = -1;
/** Build list of fields for the datasource and then adds them to it.
*/
public ScheduledOperationsDataSource(Portlet portlet) {
this.portlet = portlet;
List<DataSourceField> fields = addDataSourceFields();
addFields(fields);
}
@Override
protected List<DataSourceField> addDataSourceFields() {
List<DataSourceField> fields = super.addDataSourceFields();
DataSourceTextField resourceField = new DataSourceTextField(Field.RESOURCE_OR_GROUP.propertyName, Field.RESOURCE_OR_GROUP.title);
fields.add(resourceField);
DataSourceTextField operationField = new DataSourceTextField(Field.OPERATION.propertyName(),
Field.OPERATION.title());
fields.add(operationField);
DataSourceTextField timeField = new DataSourceTextField(Field.TIME.propertyName(), Field.TIME.title());
fields.add(timeField);
return fields;
}
/** Fetch the ProblemResource data, and populate the response object appropriately.
*
* @param request incoming request
* @param response outgoing response
*/
@Override
public void executeFetch(final DSRequest request, final DSResponse response, final Criteria unused) {
int pageSize = -1;
//retrieve current portlet display settings
if ((this.portlet != null) && (this.portlet instanceof OperationSchedulePortlet)) {
OperationSchedulePortlet operationsPortlet = (OperationSchedulePortlet) this.portlet;
//populate criteria with portlet preferences defined.
if (operationsPortlet != null) {
if (isOperationsRangeScheduleEnabled()) {
pageSize = getOperationsRangeScheduled();
operationsPortlet.getScheduledOperationsGrid().setEmptyMessage(
OperationSchedulePortlet.RANGE_DISABLED_MESSAGE_DEFAULT);
} else {//show the component, return no results and indicate that you've disabled this display
pageSize = 0;
operationsPortlet.getScheduledOperationsGrid().setEmptyMessage(
OperationSchedulePortlet.RANGE_DISABLED_MESSAGE);
response.setData(null);
response.setTotalRows(0);
//pass off for processing
processResponse(request.getRequestId(), response);
return;
}
}
}
final int pageSizeConst = pageSize;
GWTServiceLookup.getOperationService().findCurrentlyScheduledResourceOperations(pageSizeConst,
new AsyncCallback<PageList<ResourceOperationScheduleComposite>>() {
public void onFailure(Throwable throwable) {
CoreGUI.getErrorHandler().handleError(MSG.dataSource_scheduledOperations_error_fetchFailure(),
throwable);
}
public void onSuccess(final PageList<ResourceOperationScheduleComposite> resources) {
GWTServiceLookup.getOperationService().findCurrentlyScheduledGroupOperations(pageSizeConst,
new AsyncCallback<PageList<GroupOperationScheduleComposite>>() {
public void onFailure(Throwable throwable) {
CoreGUI.getErrorHandler().handleError(
MSG.dataSource_scheduledOperations_error_fetchFailure(), throwable);
}
public void onSuccess(PageList<GroupOperationScheduleComposite> groups) {
List<OperationScheduleComposite> result = new ArrayList<OperationScheduleComposite>();
if (resources != null) {
result.addAll(resources);
result.addAll(groups);
}
Collections.sort(result, new Comparator<OperationScheduleComposite>() {
public int compare(OperationScheduleComposite thisOp,
OperationScheduleComposite thatOp) {
return thisOp.getOperationNextFireTime() - thatOp.getOperationNextFireTime() < 0 ? -1
: 1;
}
});
PageControl pc = new PageControl(0, pageSizeConst);
PageList<OperationScheduleComposite> pageList = new PageList<OperationScheduleComposite>(
result, pc);
dataRetrieved(pageList, response, request);
}
});
}
});
}
@Override
protected Criteria getFetchCriteria(DSRequest request) {
// we don't use criterias for this datasource, just return null
return null;
}
protected void dataRetrieved(final PageList<OperationScheduleComposite> result, final DSResponse response,
final DSRequest request) {
HashSet<Integer> typesSet = new HashSet<Integer>();
HashSet<String> ancestries = new HashSet<String>();
for (OperationScheduleComposite composite : result) {
if (composite instanceof ResourceOperationScheduleComposite) {
ResourceOperationScheduleComposite resourceComposite = (ResourceOperationScheduleComposite) composite;
typesSet.add(resourceComposite.getResourceTypeId());
ancestries.add(resourceComposite.getAncestry());
}
}
// In addition to the types of the result resources, get the types of their ancestry
// NOTE: this may be too labor intensive in general, but since this datasource is a singleton I couldn't
// make it easily optional.
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);
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);
// for paging to work we have to specify size of full result set, but if a limit has been set,
// respect the limit
int resultSize = result.getTotalSize();
if (operationsRangeScheduled > 0 && operationsRangeScheduled < resultSize) {
resultSize = operationsRangeScheduled;
}
response.setTotalRows(resultSize);
processResponse(request.getRequestId(), response);
}
});
}
@Override
public ResourceOperationScheduleComposite copyValues(Record from) {
throw new UnsupportedOperationException("ResourceOperationScheduleComposite data is read only");
}
@Override
public ListGridRecord copyValues(OperationScheduleComposite from) {
ListGridRecord record = new ListGridRecord();
if (from instanceof ResourceOperationScheduleComposite) {
ResourceOperationScheduleComposite resource = (ResourceOperationScheduleComposite) from;
record.setAttribute(Field.RESOURCE_OR_GROUP.propertyName, resource.getResourceName());
// for ancestry handling
record.setAttribute(AncestryUtil.RESOURCE_ID, resource.getResourceId());
record.setAttribute(AncestryUtil.RESOURCE_NAME, resource.getResourceName());
record.setAttribute(AncestryUtil.RESOURCE_ANCESTRY, resource.getAncestry());
record.setAttribute(AncestryUtil.RESOURCE_TYPE_ID, resource.getResourceTypeId());
} else { // group
GroupOperationScheduleComposite resource = (GroupOperationScheduleComposite) from;
record.setAttribute(Field.RESOURCE_OR_GROUP.propertyName, resource.getGroupName());
record.setAttribute(Field.GROUP_ID.propertyName, resource.getGroupId());
record.setAttribute(Field.GROUP_TYPE.propertyName, resource.getGroupResourceTypeName());
record.setAttribute(Field.IS_AUTO_GROUP.propertyName, resource.isAutoGroup());
}
record.setAttribute("id", from.getId());
record.setAttribute(Field.OPERATION.propertyName, from.getOperationName());
record.setAttribute(Field.TIME.propertyName, new Date(from.getOperationNextFireTime()));
record.setAttribute("entity", from);
return record;
}
public boolean isOperationsRangeScheduleEnabled() {
return operationsRangeNextEnabled;
}
public void setOperationsRangeScheduleEnabled(boolean operationsRangeNextEnabled) {
this.operationsRangeNextEnabled = operationsRangeNextEnabled;
}
public int getOperationsRangeScheduled() {
return operationsRangeScheduled;
}
public void setOperationsRangeScheduled(int operationsRangeScheduled) {
this.operationsRangeScheduled = operationsRangeScheduled;
}
}