/*
* Lokomo OneCMDB - An Open Source Software for Configuration
* Management of Datacenter Resources
*
* Copyright (C) 2006 Lokomo Systems AB
*
* 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; either version 2 of the License, or (at
* your option) any later version.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* Lokomo Systems AB can be contacted via e-mail: info@lokomo.com or via
* paper mail: Lokomo Systems AB, Sv�rdv�gen 27, SE-182 33
* Danderyd, Sweden.
*
*/
package org.onecmdb.ui.gwt.desktop.client.widget;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.jsp.tagext.PageData;
import org.onecmdb.ui.gwt.desktop.client.fixes.MyMessageBox;
import org.onecmdb.ui.gwt.desktop.client.mvc.CMDBEvents;
import org.onecmdb.ui.gwt.desktop.client.service.CMDBAsyncCallback;
import org.onecmdb.ui.gwt.desktop.client.service.change.ChangeItem;
import org.onecmdb.ui.gwt.desktop.client.service.change.ChangeRecord;
import org.onecmdb.ui.gwt.desktop.client.service.change.ChangeServiceFactory;
import org.onecmdb.ui.gwt.desktop.client.service.model.CIModel;
import org.onecmdb.ui.gwt.desktop.client.service.model.CMDBSession;
import org.onecmdb.ui.gwt.desktop.client.service.model.ModelServiceFactory;
import org.onecmdb.ui.gwt.desktop.client.service.model.StoreResult;
import org.onecmdb.ui.gwt.desktop.client.service.model.ValueModel;
import org.onecmdb.ui.gwt.desktop.client.service.model.mdr.MDRHistoryState;
import org.onecmdb.ui.gwt.desktop.client.utils.HTMLGenerator;
import com.extjs.gxt.ui.client.Style.Scroll;
import com.extjs.gxt.ui.client.core.XTemplate;
import com.extjs.gxt.ui.client.data.BasePagingLoadConfig;
import com.extjs.gxt.ui.client.data.BasePagingLoadResult;
import com.extjs.gxt.ui.client.data.BasePagingLoader;
import com.extjs.gxt.ui.client.data.LoadEvent;
import com.extjs.gxt.ui.client.data.PagingLoadResult;
import com.extjs.gxt.ui.client.data.RpcProxy;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.event.LoadListener;
import com.extjs.gxt.ui.client.event.SelectionListener;
import com.extjs.gxt.ui.client.event.WindowEvent;
import com.extjs.gxt.ui.client.store.GroupingStore;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.Dialog;
import com.extjs.gxt.ui.client.widget.Info;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
import com.extjs.gxt.ui.client.widget.MessageBox;
import com.extjs.gxt.ui.client.widget.PagingToolBar;
import com.extjs.gxt.ui.client.widget.TabItem;
import com.extjs.gxt.ui.client.widget.TabPanel;
import com.extjs.gxt.ui.client.widget.Window;
import com.extjs.gxt.ui.client.widget.button.Button;
import com.extjs.gxt.ui.client.widget.grid.CheckColumnConfig;
import com.extjs.gxt.ui.client.widget.grid.ColumnConfig;
import com.extjs.gxt.ui.client.widget.grid.ColumnModel;
import com.extjs.gxt.ui.client.widget.grid.Grid;
import com.extjs.gxt.ui.client.widget.grid.GridGroupRenderer;
import com.extjs.gxt.ui.client.widget.grid.GroupColumnData;
import com.extjs.gxt.ui.client.widget.grid.GroupingView;
import com.extjs.gxt.ui.client.widget.grid.RowExpander;
import com.extjs.gxt.ui.client.widget.layout.FitLayout;
import com.extjs.gxt.ui.client.widget.layout.RowData;
import com.extjs.gxt.ui.client.widget.layout.RowLayout;
import com.extjs.gxt.ui.client.widget.toolbar.FillToolItem;
import com.extjs.gxt.ui.client.widget.toolbar.SeparatorToolItem;
import com.extjs.gxt.ui.client.widget.toolbar.TextToolItem;
import com.extjs.gxt.ui.client.widget.toolbar.ToolBar;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HTML;
public class CompareGridWidget extends LayoutContainer {
private CIModel mdrConfigHistory;
private CIModel mdr;
private CIModel mdrConfig;
private BasePagingLoader loader;
private List<? extends ChangeItem> resultSet;
private ChangeRecord changeRecord;
private boolean rejectEnabled = true;
private boolean deleteEnabled = true;
private HTML overviewHtml;
private GroupingStore<ChangeItem> store;
public CompareGridWidget() {
}
public boolean isDeleteEnabled() {
return deleteEnabled;
}
public void setDeleteEnabled(boolean deleteEnabled) {
this.deleteEnabled = deleteEnabled;
}
public boolean isRejectEnabled() {
return rejectEnabled;
}
public void setRejectEnabled(boolean rejectEnabled) {
this.rejectEnabled = rejectEnabled;
}
@Override
protected void onRender(Element parent, int index) {
// TODO Auto-generated method stub
super.onRender(parent, index);
init();
}
public void init() {
RpcProxy proxy = new RpcProxy() {
@Override
public void load(Object loadConfig, final AsyncCallback callback) {
final BasePagingLoadConfig config = (BasePagingLoadConfig)loadConfig;
if (resultSet != null) {
int offset = config.getOffset();
int limit = config.getLimit();
List<ChangeItem> items = new ArrayList<ChangeItem>();
for (int i = offset; i < (limit+offset); i++) {
if (i >= resultSet.size()) {
break;
}
ChangeItem item = resultSet.get(i);
items.add(item);
}
PagingLoadResult result = new BasePagingLoadResult(items, offset, resultSet.size());
callback.onSuccess(result);
return;
}
ChangeServiceFactory.get().loadChanges(CMDBSession.get().getToken(), mdr, mdrConfig, mdrConfigHistory, null, new CMDBAsyncCallback<ChangeRecord>() {
@Override
public void onSuccess(ChangeRecord arg0) {
changeRecord = arg0;
resultSet = arg0.getChangeItems();
if (resultSet.size() == 0) {
Info.display("MDR compare info", "No changes found");
//("Changes", "No changes found!");
}
int offset = config.getOffset();
int limit = config.getLimit();
List<ChangeItem> items = new ArrayList<ChangeItem>();
for (int i = offset; i < limit; i++) {
if (i >= resultSet.size()) {
break;
}
ChangeItem item = resultSet.get(i);
items.add(item);
}
PagingLoadResult result = new BasePagingLoadResult(items, offset, resultSet.size());
callback.onSuccess(result);
}
});
}
};
// loader
loader = new BasePagingLoader(proxy);
loader.setRemoteSort(false);
loader.addLoadListener(new LoadListener() {
@Override
public void loaderLoad(LoadEvent le) {
updateOverview(le);
}
@Override
public void loaderLoadException(LoadEvent le) {
updateOverview(le);
}
});
final PagingToolBar toolBar = new PagingToolBar(1000);
store = new GroupingStore<ChangeItem>(loader);
store.groupBy("derivedFrom");
//store.setRemoteGroup(true);
TextToolItem reject = new TextToolItem("Reject", "reject-icon");
reject.setToolTip("Mark this data as rejected");
reject.setEnabled(this.rejectEnabled);
reject.addSelectionListener(new SelectionListener<ComponentEvent>() {
@Override
public void componentSelected(ComponentEvent ce) {
// TODO Auto-generated method stub
final MessageBox commitInfo = MessageBox.wait("Progress",
"Reject your data, please wait...", "Rejecting...");
CIModel copy = mdrConfigHistory.copy();
ValueModel mv = mdrConfigHistory.getValue("status");
if (mv == null) {
mv = new ValueModel();
mv.setAlias("status");
mv.setIsComplex(false);
mdrConfigHistory.setValue(mv.getAlias(), mv);
}
mv.setValue(MDRHistoryState.REJECTED);
ArrayList<CIModel> local = new ArrayList<CIModel>();
ArrayList<CIModel> base = new ArrayList<CIModel>();
local.add(mdrConfigHistory);
base.add(copy);
ModelServiceFactory.get().store(CMDBSession.get().getDefaultCMDB_MDR(), CMDBSession.get().getToken(), local, base, new CMDBAsyncCallback<StoreResult>() {
public void onSuccess(StoreResult result) {
commitInfo.close();
if (result.isRejected()) {
MessageBox.alert("Failed when rejecting", result.getRejectCause(), null);
return;
}
MyMessageBox.info("Rejected", "The history entry has been marked as rejected", new Listener<WindowEvent>() {
public void handleEvent(WindowEvent be) {
// Will reload and verify.
resultSet = null;
loader.load();
}
});
}
});
}
});
TextToolItem commit = new TextToolItem("Commit to CMDB", "commit-icon");
commit.setToolTip("Commit the selected data to the cmdb.");
commit.addSelectionListener(new SelectionListener<ComponentEvent>() {
@Override
public void componentSelected(ComponentEvent ce) {
final MessageBox commitInfo = MessageBox.wait("Progress",
"Commit your data, please wait...", "Committing...");
store.commitChanges();
ChangeServiceFactory.get().commit(CMDBSession.get().getToken(), mdr, mdrConfig, mdrConfigHistory, store.getModels(), new AsyncCallback<StoreResult>() {
public void onFailure(Throwable arg0) {
commitInfo.close();
ExceptionErrorDialog.showError("Can't Save", arg0);
}
public void onSuccess(StoreResult result) {
commitInfo.close();
if (result.isRejected()) {
MessageBox.alert("Commit Failed", result.getRejectCause(), null);
return;
}
MyMessageBox.info("Committed", HTMLGenerator.toHTML(result), new Listener<WindowEvent>() {
public void handleEvent(WindowEvent be) {
if (getParent() instanceof Window) {
((Window)getParent()).close();
}
fireEvent(CMDBEvents.COMMIT_EVENT);
}
});
}
});
}
});
TextToolItem delete = new TextToolItem("Remove", "delete-icon");
delete.setToolTip("Remove all previously committed CIs.");
delete.setEnabled(this.deleteEnabled);
delete.addSelectionListener(new SelectionListener<ComponentEvent>() {
@Override
public void componentSelected(ComponentEvent ce) {
MessageBox.confirm("Remove", "Remove previously committed CIs?", new Listener<WindowEvent>() {
public void handleEvent(WindowEvent be) {
Button button = be.buttonClicked;
if (button.getItemId().equals(Dialog.YES)) {
// Do remove...
doRemove();
}
}
});
}
});
TextToolItem cancel = new TextToolItem("Cancel", "cancel-icon");
cancel.addSelectionListener(new SelectionListener<ComponentEvent>() {
@Override
public void componentSelected(ComponentEvent ce) {
if (getParent() instanceof Window) {
((Window)getParent()).close();
}
}
});
ToolBar buttonBar = new ToolBar();
buttonBar.add(new FillToolItem());
buttonBar.add(commit);
buttonBar.add(new SeparatorToolItem());
buttonBar.add(cancel);
buttonBar.add(delete);
//buttonBar.add(reject);
//toolBar.add(commit);
toolBar.bind(loader);
ColumnConfig derivedFrom = new ColumnConfig("derivedFrom", "Derived From", 100);
ColumnConfig ci = new ColumnConfig("alias", "CI", 100);
ColumnConfig status = new ColumnConfig("status", "Status", 50);
ColumnConfig type = new ColumnConfig("type", "Type", 50);
ColumnConfig info = new ColumnConfig("info", "Info", 50);
List<ColumnConfig> config = new ArrayList<ColumnConfig>();
XTemplate tpl = XTemplate.create("<b>Summary:</b> {summary}");
RowExpander expander = new RowExpander();
expander.setTemplate(tpl);
config.add(expander);
CheckColumnConfig exclude = new CheckColumnConfig("include", "Include", 30);
config.add(exclude);
config.add(type);
config.add(ci);
config.add(derivedFrom);
config.add(status);
config.add(info);
final ColumnModel cm = new ColumnModel(config);
GroupingView view = new GroupingView();
view.setForceFit(true);
view.setGroupRenderer(new GridGroupRenderer() {
public String render(GroupColumnData data) {
String style = "#00cd00";
if (data.field.equals("status") && data.group.startsWith("ERROR:")) {
style = "#ff1010";
}
// check how many...
int totalCount = 0;
if (resultSet != null) {
for (ChangeItem c : resultSet) {
String id = data.field;
Object o = c.get(id);
if (o != null) {
if (o.equals(data.group)) {
totalCount++;
}
}
}
}
String f = cm.getColumnById(data.field).getHeader();
String l = data.models.size() == 1 ? "Item" : "Items";
return f + ": " + "<span style='color:" + style + "'>" + data.group + "</span> " + data.models.size() + "(" + totalCount +") " + l;
}
});
Grid grid = new Grid(store, cm);
grid.setView(view);
grid.setBorders(true);
grid.addPlugin(exclude);
grid.addPlugin(expander);
grid.getView().setForceFit(true);
setLayout(new RowLayout());
ContentPanel gridPanel = new ContentPanel();
gridPanel.setHeaderVisible(false);
gridPanel.setLayout(new FitLayout());
gridPanel.setTopComponent(toolBar);
gridPanel.add(grid);
TabPanel tab = new TabPanel();
TabItem overview = new TabItem("Overview");
overview.setLayout(new FitLayout());
ContentPanel cp = new ContentPanel();
cp.setHeaderVisible(false);
cp.setScrollMode(Scroll.AUTO);
cp.setLayoutOnChange(true);
cp.setLayout(new FitLayout());
overviewHtml = new HTML("<i>Loading...</i>");
overviewHtml.setStyleName("property-panel-background");
cp.setStyleName("property-panel-background");
cp.add(overviewHtml);
overview.add(cp);
TabItem details = new TabItem("Details");
details.setLayout(new FitLayout());
details.add(gridPanel);
tab.add(overview);
tab.add(details);
add(tab, new RowData(1, 1));
add(buttonBar, new RowData(1,-1));
layout();
}
private void doRemove() {
final MessageBox commitInfo = MessageBox.wait("Progress",
"Delete your data, please wait...", "Deleting...");
store.commitChanges();
ChangeServiceFactory.get().delete(CMDBSession.get().getToken(), mdr, mdrConfig, mdrConfigHistory, store.getModels(), new AsyncCallback<StoreResult>() {
public void onFailure(Throwable arg0) {
commitInfo.close();
ExceptionErrorDialog.showError("Can't Delete", arg0);
}
public void onSuccess(StoreResult result) {
commitInfo.close();
if (result.isRejected()) {
MessageBox.alert("Delete Failed", result.getRejectCause(), null);
return;
}
MyMessageBox.info("Deleted", HTMLGenerator.toHTML(result), new Listener<WindowEvent>() {
public void handleEvent(WindowEvent be) {
// Will reload and verify.
resultSet = null;
loader.load();
}
});
}
});
}
public void setModels(CIModel mdr, CIModel config, CIModel history) {
this.mdrConfigHistory = history;
this.mdr = mdr;
this.mdrConfig = config;
resultSet = null;
if (overviewHtml != null) {
overviewHtml.setHTML("<i>Loading...</i>");
}
if (loader != null) {
loader.load();
}
}
protected void updateOverview(LoadEvent le) {
StringBuffer b = new StringBuffer();
if (le.exception != null) {
b.append("<p><b>Error</b><p/>");
b.append("<p>" + le.exception.toString() + "</p>");
}
if (le.data instanceof PagingLoadResult) {
List<? extends ChangeItem> result = resultSet;
if (result == null) {
result = new ArrayList<ChangeItem>();
}
if (changeRecord != null) {
b.append(changeRecord.getSummary());
}
HashMap<String, ResultStatusEntry> resultMap = new HashMap<String, ResultStatusEntry>();
int totalChanges = result.size();
for (ChangeItem item : result) {
String status = item.get("status");
String type = item.get("type");
updateResultStatus(resultMap, status, type);
}
b.append("<p><b>Compare result</b><br><br>");
for (String status : resultMap.keySet()) {
ResultStatusEntry resultType = resultMap.get(status);
if (status.toLowerCase().contains("error")) {
b.append("<span style='color:#ff1010'>");
}
b.append(resultType.occurenc + " " + status + " CIs (" + resultType.templates + " templates, " + resultType.instances + " instances)<br/>");
if (status.toLowerCase().contains("error")) {
b.append("</span>");
}
}
b.append("----------------------<br>");
b.append("<b>" + totalChanges + "</b> Total changes<br>");
b.append("</p>");
}
overviewHtml.setHTML(b.toString());
layout();
}
private void updateResultStatus(Map<String, ResultStatusEntry> map, String status, String type) {
ResultStatusEntry result = map.get(status);
if (result == null) {
result = new ResultStatusEntry();
result.status = status;
map.put(status, result);
}
if (type.equalsIgnoreCase("template")) {
result.templates++;
} else {
result.instances++;
}
result.occurenc++;
}
class ResultStatusEntry {
String status;
int templates;
int instances;
int occurenc;
}
}