/*
* 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.util.message;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Timer;
import com.smartgwt.client.data.SortSpecifier;
import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.types.ListGridFieldType;
import com.smartgwt.client.types.SelectionStyle;
import com.smartgwt.client.types.SortDirection;
import com.smartgwt.client.types.TimeDisplayFormat;
import com.smartgwt.client.types.VerticalAlignment;
import com.smartgwt.client.widgets.Window;
import com.smartgwt.client.widgets.events.CloseClickEvent;
import com.smartgwt.client.widgets.events.CloseClickHandler;
import com.smartgwt.client.widgets.events.DoubleClickEvent;
import com.smartgwt.client.widgets.events.DoubleClickHandler;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.fields.FormItemIcon;
import com.smartgwt.client.widgets.form.fields.StaticTextItem;
import com.smartgwt.client.widgets.grid.HoverCustomizer;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.grid.SortNormalizer;
import org.rhq.coregui.client.CoreGUI;
import org.rhq.coregui.client.components.table.AbstractTableAction;
import org.rhq.coregui.client.components.table.Table;
import org.rhq.coregui.client.components.table.TableActionEnablement;
import org.rhq.coregui.client.components.table.TimestampCellFormatter;
import org.rhq.coregui.client.menu.MenuBarView;
import org.rhq.coregui.client.util.Log;
import org.rhq.coregui.client.util.enhanced.EnhancedIButton.ButtonColor;
import org.rhq.coregui.client.util.message.Message.Severity;
/**
* Message Center view that shows the latest messages generated by the app.
* This is a Table and can therefore be a member to any layout. However, it can
* also be displayed in its own non-modal dialog window. If you want this table
* shown in a dialog window, call {@link #showMessageCenterWindow()}.
*
* Note: this class has to be very careful about catching any and all exceptions.
* Otherwise, an uncaught exception will cause a flooding of global exception
* messages (since the unhandled exception handler in CoreGUI will recursively
* call into this message center).
*
* @author John Mazzitelli
*/
@SuppressWarnings("unchecked")
public class MessageCenterView extends Table implements MessageCenter.MessageListener {
public static final String TABLE_TITLE = MSG.view_messageCenter_messageTitle();
private static final String FIELD_TIME = "time";
private static final String FIELD_SEVERITY = "severity";
private static final String FIELD_CONCISEMESSAGE = "conciseMessage";
private static final String FIELD_OBJECT = "object";
private MessageCenterWindow window;
public MessageCenterView() {
super(MSG.view_messageCenter_messageTitle(), null, new SortSpecifier[] { new SortSpecifier(FIELD_TIME,
SortDirection.DESCENDING) }, null, false);
CoreGUI.getMessageCenter().addMessageListener(this);
}
/**
* This will popup a non-modal window with the messages in a list.
*/
public void showMessageCenterWindow() {
try {
if (window == null) {
window = new MessageCenterWindow();
window.addItem(this); // Use addItem(), not addMember(), when adding a widget to a Window!
window.addCloseClickHandler(new CloseClickHandler() {
@Override
public void onCloseClick(CloseClickEvent event) {
try {
window.hide();
} catch (Throwable e) {
Log.warn("Cannot hide message center", e);
}
}
});
}
window.show();
markForRedraw(); // need this to ensure the list grid rows are selectable
} catch (Throwable e) {
Log.error("Cannot show message center window", e);
}
}
private void updateMsgCenterButtonContent() {
Element btn = DOM.getElementById(MenuBarView.BTN_MSG_CENTER_ID);
btn.setInnerHTML(MenuBarView.MSG_CENTER_BTN_CONTENT+String.valueOf(CoreGUI.getMessageCenter().getMessages().size()));
}
@Override
public void onMessage(final Message message) {
try {
if (!message.isTransient()) {
refresh();
if (window != null) {
window.blink();
}
updateMsgCenterButtonContent();
}
} catch (Throwable e) {
Log.error("Cannot process message", e);
}
}
public void reset() {
if (window != null) {
window.hide();
}
refresh();
}
@Override
protected void configureTable() {
getListGrid().setEmptyMessage(MSG.view_messageCenter_noRecentMessages());
updateTitleCanvas(MSG.view_messageCenter_lastNMessages(String.valueOf(CoreGUI.getMessageCenter()
.getMaxMessages())));
ListGridField severityField = new ListGridField(FIELD_SEVERITY);
severityField.setType(ListGridFieldType.ICON);
severityField.setAlign(Alignment.CENTER);
severityField.setShowValueIconOnly(true);
HashMap<String, String> severityIcons = new HashMap<String, String>(5);
severityIcons.put(Severity.Blank.name(), getSeverityIcon(Severity.Blank));
severityIcons.put(Severity.Info.name(), getSeverityIcon(Severity.Info));
severityIcons.put(Severity.Warning.name(), getSeverityIcon(Severity.Warning));
severityIcons.put(Severity.Error.name(), getSeverityIcon(Severity.Error));
severityIcons.put(Severity.Fatal.name(), getSeverityIcon(Severity.Fatal));
severityField.setValueIcons(severityIcons);
severityField.setShowHover(true);
severityField.setHoverCustomizer(new HoverCustomizer() {
@Override
public String hoverHTML(Object value, ListGridRecord record, int rowNum, int colNum) {
try {
Severity severity = ((Message) record.getAttributeAsObject(FIELD_OBJECT)).getSeverity();
switch (severity) {
case Info:
return MSG.common_severity_info();
case Warning:
return MSG.common_severity_warn();
case Error:
return MSG.common_severity_error();
case Fatal:
return MSG.common_severity_fatal();
}
} catch (Throwable e) {
Log.error("Cannot get severity hover", e);
}
return null;
}
});
severityField.setSortNormalizer(new SortNormalizer() {
@Override
public Object normalize(ListGridRecord record, String fieldName) {
try {
Severity severity = ((Message) record.getAttributeAsObject(FIELD_OBJECT)).getSeverity();
return Integer.valueOf(severity.ordinal());
} catch (Throwable e) {
Log.error("Cannot get sort nomalizer", e);
}
return Integer.valueOf(0);
}
});
ListGridField timeField = new ListGridField(FIELD_TIME, MSG.view_messageCenter_messageTime());
timeField.setType(ListGridFieldType.TIME);
timeField.setAttribute("displayFormat", TimeDisplayFormat.TOPADDEDTIME);
timeField.setAlign(Alignment.LEFT);
timeField.setShowHover(true);
timeField.setHoverCustomizer(TimestampCellFormatter.getHoverCustomizer(FIELD_TIME));
ListGridField messageField = new ListGridField(FIELD_CONCISEMESSAGE, MSG.common_title_message());
severityField.setWidth(25);
timeField.setWidth("15%");
messageField.setWidth("*");
getListGrid().setFields(severityField, timeField, messageField);
setListGridDoubleClickHandler(new DoubleClickHandler() {
@Override
public void onDoubleClick(DoubleClickEvent event) {
try {
ListGrid listGrid = (ListGrid) event.getSource();
ListGridRecord[] selectedRows = listGrid.getSelectedRecords();
if (selectedRows != null && selectedRows.length > 0) {
Message message = (Message) selectedRows[0].getAttributeAsObject(FIELD_OBJECT); // show the first selected
showDetails(message);
}
} catch (Throwable e) {
Log.error("Cannot show details for message", e);
}
}
});
addTableAction(MSG.common_button_delete(), MSG.common_msg_areYouSure(), ButtonColor.RED,
new AbstractTableAction(TableActionEnablement.ANY) {
@Override
public void executeAction(ListGridRecord[] selection, Object actionValue) {
try {
for (ListGridRecord record : selection) {
Object doomed = record.getAttributeAsObject(FIELD_OBJECT);
CoreGUI.getMessageCenter().getMessages().remove(doomed);
}
refresh();
updateMsgCenterButtonContent();
} catch (Throwable e) {
Log.error("Cannot delete messages", e);
}
}
});
addTableAction(MSG.common_button_delete_all(), MSG.common_msg_areYouSure(), ButtonColor.RED,
new AbstractTableAction(TableActionEnablement.ALWAYS) {
@Override
public void executeAction(ListGridRecord[] selection, Object actionValue) {
try {
CoreGUI.getMessageCenter().getMessages().clear();
refresh();
updateMsgCenterButtonContent();
} catch (Throwable e) {
Log.error("Cannot delete all messages", e);
}
}
});
LinkedHashMap<String, Integer> maxMessagesMap = new LinkedHashMap<String, Integer>();
maxMessagesMap.put("10", Integer.valueOf("10"));
maxMessagesMap.put("25", Integer.valueOf("25"));
maxMessagesMap.put("50", Integer.valueOf("50"));
maxMessagesMap.put("100", Integer.valueOf("100"));
maxMessagesMap.put("200", Integer.valueOf("200"));
addTableAction(MSG.view_messageCenter_maxMessages(), null, maxMessagesMap, ButtonColor.GRAY,
new AbstractTableAction(TableActionEnablement.ALWAYS) {
@Override
public void executeAction(ListGridRecord[] selection, Object actionValue) {
try {
Integer maxSize = (Integer) actionValue;
CoreGUI.getMessageCenter().setMaxMessages(maxSize.intValue());
updateTitleCanvas(MSG.view_messageCenter_lastNMessages(maxSize.toString()));
refresh();
} catch (Throwable e) {
Log.error("Cannot set max messages", e);
}
}
});
// initial population of the list with current messages
try {
refresh();
} catch (Throwable e) {
Log.error("Cannot perform initial refresh", e);
}
}
@Override
protected SelectionStyle getDefaultSelectionStyle() {
return SelectionStyle.MULTIPLE;
}
@Override
public void refresh() {
try {
super.refresh();
if ((getListGrid() != null) && (CoreGUI.getMessageCenter().getMessages() != null)) {
getListGrid().setRecords(transform(CoreGUI.getMessageCenter().getMessages()));
}
refreshTableInfo();
} catch (Throwable e) {
Log.error("Cannot refresh messages", e);
}
}
private ListGridRecord[] transform(List<Message> list) {
ListGridRecord[] results = new ListGridRecord[list.size()];
for (int i = 0; i < list.size(); i++) {
results[i] = transform(list.get(i));
}
return results;
}
private ListGridRecord transform(Message msg) {
ListGridRecord record = new ListGridRecord();
record.setAttribute(FIELD_TIME, msg.fired);
record.setAttribute(FIELD_SEVERITY, (msg.severity != null) ? msg.severity.name() : Severity.Info.name());
record.setAttribute(FIELD_CONCISEMESSAGE, msg.conciseMessage);
record.setAttribute(FIELD_OBJECT, msg);
return record;
}
/**
* This is a static utility method that is package protected so the message center view
* and the message bar can pop up a dialog showing a message's details.
*
* @param message the message whose details are to be shown
*/
static void showDetails(Message message) {
if (message == null) {
return;
}
DynamicForm form = new DynamicForm();
form.setWrapItemTitles(false);
form.setAlign(Alignment.LEFT);
StaticTextItem title = new StaticTextItem("theMessage", MSG.common_title_message());
title.setValue(message.conciseMessage);
StaticTextItem severity = new StaticTextItem("severity", MSG.view_messageCenter_messageSeverity());
FormItemIcon severityIcon = new FormItemIcon();
severityIcon.setSrc(getSeverityIcon(message.severity));
severity.setIcons(severityIcon);
severity.setValue(message.severity.name());
StaticTextItem date = new StaticTextItem("time", MSG.view_messageCenter_messageTime());
date.setValue(TimestampCellFormatter.format(message.fired, TimestampCellFormatter.DATE_TIME_FORMAT_FULL));
StaticTextItem rootCause = new StaticTextItem("rootCause", MSG.view_messageCenter_messageRootCause());
rootCause.setValue(message.rootCauseMessage);
rootCause.setVisible(message.rootCauseMessage != null);
StaticTextItem detail = new StaticTextItem("detail", MSG.view_messageCenter_messageDetail());
detail.setTitleVAlign(VerticalAlignment.TOP);
detail.setValue(message.detailedMessage);
form.setItems(title, severity, date, rootCause, detail);
final Window dialogWin = new Window();
dialogWin.setTitle(MSG.common_title_message());
dialogWin.setWidth(600);
dialogWin.setHeight(400);
dialogWin.setIsModal(true);
dialogWin.setShowModalMask(true);
dialogWin.setCanDragResize(true);
dialogWin.setShowMaximizeButton(true);
dialogWin.setShowMinimizeButton(false);
dialogWin.centerInPage();
dialogWin.addItem(form);
dialogWin.show();
dialogWin.addCloseClickHandler(new CloseClickHandler() {
@Override
public void onCloseClick(CloseClickEvent event) {
dialogWin.destroy();
}
});
}
private static String getSeverityIcon(Message.Severity severity) {
if (severity == null) {
severity = Severity.Blank;
}
return severity.getIcon();
}
static class MessageCenterWindow extends Window {
private Timer blinkTimer;
public MessageCenterWindow() {
super();
setTitle(TABLE_TITLE);
setShowMinimizeButton(true);
setShowMaximizeButton(true);
setShowCloseButton(true);
setIsModal(true);
setShowModalMask(true);
setWidth(700);
setHeight(300);
setShowResizer(true);
setCanDragResize(true);
centerInPage();
final String origColor = getBodyColor();
blinkTimer = new Timer() {
@Override
public void run() {
try {
setBodyColor(origColor);
setTitle(TABLE_TITLE);
redraw();
} catch (Throwable e) {
Log.error("Blink timer failed", e);
}
}
};
}
public void blink() {
try {
// window.flash() isn't working so do it ourselves
if (getMinimized()) {
setTitle(TABLE_TITLE + " *");
} else {
setBodyColor(getHiliteBodyColor());
}
redraw();
blinkTimer.schedule(250);
} catch (Throwable e) {
Log.error("Cannot blink message center window", e);
}
}
}
}