/* * 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); } } } }