/*
* Copyright 2003,2004 Colin Crist
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package hermes.browser.components;
import hermes.Hermes;
import hermes.HermesException;
import hermes.HermesWatchListener;
import hermes.HermesWatchManager;
import hermes.browser.HermesBrowser;
import hermes.browser.IconCache;
import hermes.browser.model.QueueWatchTableModel;
import hermes.browser.model.WatchInfo;
import hermes.config.DestinationConfig;
import hermes.config.WatchConfig;
import hermes.impl.DestinationConfigKeyWrapper;
import hermes.swing.SwingRunner;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import javax.jms.JMSException;
import javax.naming.NamingException;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import org.apache.log4j.Logger;
import com.jidesoft.docking.DockContext;
import com.jidesoft.docking.DockableFrame;
import com.jidesoft.docking.event.DockableFrameAdapter;
import com.jidesoft.docking.event.DockableFrameEvent;
import com.jidesoft.swing.JidePopupMenu;
import com.jidesoft.swing.JideScrollPane;
/**
* A sortable table that shows interesting statistics about a queue/topics.
*
* @author colincrist@hermesjms.com
* @version $Id: WatchDockableFrame.java,v 1.9 2005/08/07 09:02:51 colincrist
* Exp $
*/
public class WatchDockableFrame extends DockableFrame implements HermesWatchListener
{
/**
*
*/
private static final long serialVersionUID = -2892316512453404493L;
private static final Timer flashingTimer = new Timer();
private static final Logger log = Logger.getLogger(WatchDockableFrame.class);
private static final long DEFAULT_AGE_ALERT = 0;
private static final int DEFAULT_DEPTH_ALERT = 0;
private QueueWatchTableModel model;
private WatchConfig config;
private JPopupMenu popupMenu;
private JMenuItem stopItem;
private JMenuItem stopAllItem;
private JMenuItem saveItem;
private JMenuItem browseItem;
private JMenuItem updateNow;
private JMenuItem truncateItem;
private JMenuItem expandAll;
private JMenuItem collapseAll;
private MouseAdapter mouseListener;
private WatchTable table;
private JideScrollPane tableScrollPane = new JideScrollPane();
private boolean keepRunning = true;
private boolean renaming = false;
private TimerTask flashingTimerTask;
private HermesWatchManager watchManager = new HermesWatchManager();
public WatchDockableFrame(WatchConfig config)
{
super(config.getId(), IconCache.getIcon("hermes.watch"));
this.config = config;
setAvailableButtons(DockableFrame.BUTTON_CLOSE | DockableFrame.BUTTON_MAXIMIZE | DockableFrame.BUTTON_AUTOHIDE | DockableFrame.BUTTON_FLOATING);
getContext().setInitMode(DockContext.STATE_FRAMEDOCKED);
getContext().setInitSide(DockContext.DOCK_SIDE_EAST);
flashingTimerTask = new TimerTask()
{
public void run()
{
maybeDoFlash();
}
};
flashingTimer.schedule(flashingTimerTask, 1000, 1000);
init();
}
public void close()
{
watchManager.close();
model.removeAll();
flashingTimerTask.cancel();
}
public void updateNow()
{
watchManager.updateNow();
}
/**
* Called to update the configuration into the model.
*/
public void updateConfig()
{
if (config.getUpdateFrequency() < 1000)
{
config.setUpdateFrequency(1000L);
Hermes.ui.getDefaultMessageSink().add("Minimum watch frequency is 1000ms");
}
config.getDestination().clear();
config.getDestination().addAll(model.getDestinationWatchConfigs());
}
/**
* Add a watch on all the destinations configured on a Hermes.
*
* @param hermes
*/
public void addWatch(final Hermes hermes)
{
for (Iterator iter = hermes.getDestinations(); iter.hasNext();)
{
DestinationConfig dConfig = (DestinationConfig) iter.next();
addWatch(hermes.getId(), dConfig);
}
}
public void removeWatch(String hermesId, String destinationName)
{
final WatchInfo info = model.findWatchInfo(hermesId, destinationName);
if (info != null)
{
log.debug("removing hermesId=" + hermesId + " destinationName=" + destinationName);
if (!SwingUtilities.isEventDispatchThread())
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
model.removeRow(info);
}
});
}
else
{
model.removeRow(info);
}
}
else
{
log.error("cannot remove hermesId=" + hermesId + " destinationName=" + destinationName);
}
}
/**
* Add a watch for the hermes/destination, alerting after depthAlert messages
* are on the queue/topic or the top message is older than ageAlert ms.
*
* @param hermes
* @param destinationName
* @param depthAlert
* @param ageAlert
*/
private void addWatch(String hermesId, DestinationConfig dConfig, int depthAlert, long ageAlert)
{
try
{
final WatchInfo info = new WatchInfo(hermesId, dConfig);
final Hermes hermes = (Hermes) HermesBrowser.getBrowser().getContext().lookup(hermesId);
info.setAgeAlert(ageAlert);
info.setDepthAlert(depthAlert);
watchManager.addWatch(hermes, dConfig, this);
SwingRunner.invokeLater(new Runnable()
{
public void run()
{
model.addRow(info);
}
});
}
catch (Exception ex)
{
log.error("in AddWatch(): " + ex.getMessage(), ex);
}
}
public void addWatch(String hermesId, DestinationConfig dConfig)
{
addWatch(hermesId, dConfig, HermesWatchManager.DEFAULT_DEPTH_ALERT, HermesWatchManager.DEFAULT_AGE_ALERT);
}
private void maybeDoFlash()
{
Runnable run = null;
if (model != null && !model.hasAlert())
{
run = new Runnable()
{
public void run()
{
if (config != null)
{
getDockingManager().denotifyFrame(config.getId());
}
}
};
}
else
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
getDockingManager().notifyFrame(config.getId());
}
});
}
if (run != null)
{
SwingUtilities.invokeLater(run);
}
}
/**
* Initialize.
*/
private void init()
{
model = new QueueWatchTableModel();
model.addColumn("Session");
model.addColumn("Destination");
model.addColumn("Durable") ;
model.addColumn("Depth");
model.addColumn("Oldest");
model.addTableModelListener(new TableModelListener()
{
public void tableChanged(TableModelEvent e)
{
table.resort();
}
});
table = new WatchTable(model, config.isShowAge());
table.setComponentFactory(model);
table.setSortable(true);
table.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
mouseListener = new MouseAdapter()
{
public void mousePressed(MouseEvent e)
{
if (SwingUtilities.isRightMouseButton(e))
{
//
// Make a right-click also change the selection path
maybeDoPopup(e);
}
}
public void mouseClicked(MouseEvent e)
{
maybeDoBrowse(e);
}
};
getPopupMenu();
table.addMouseListener(mouseListener);
tableScrollPane.setViewportView(table);
tableScrollPane.addMouseListener(mouseListener);
//
// Its a list of session/destination pairs.
for (Iterator iter = config.getDestination().iterator(); iter.hasNext();)
{
try
{
final DestinationConfig dConfig = (DestinationConfig) iter.next();
if (dConfig.getMyHermes() != null)
{
addWatch(dConfig.getMyHermes(), dConfig, config.getDefaultDepthAlertThreshold(), config.getDefaultAgeAlertThreshold());
}
else
{
log.error("Discarded watch with a null Hermes") ;
}
}
catch (Exception e1)
{
log.error("could not start watch: " + e1.getMessage(), e1);
}
}
getContentPane().setLayout(new BorderLayout());
getContentPane().add(tableScrollPane);
addDockableFrameListener(new DockableFrameAdapter()
{
public void dockableFrameShown(DockableFrameEvent arg0)
{
log.debug("frame " + config.getId() + " visible, updating");
watchManager.updateNow();
}
});
}
public boolean doBrowse()
{
try
{
WatchInfo info = model.getRow(table.getActualRowAt(table.getSelectedRows()[0]));
if (info != null)
{
if (info.getE() == null)
{
Hermes hermes = null;
try
{
hermes = (Hermes) HermesBrowser.getBrowser().getContext().lookup(info.getHermesId());
}
catch (NamingException e)
{
log.error(e.getMessage(), e);
HermesBrowser.getBrowser().showErrorDialog("Cannot browse: ", e);
}
HermesBrowser.getBrowser().getActionFactory().createQueueBrowseAction(hermes, info.getConfig());
return true;
}
else
{
JOptionPane.showMessageDialog(HermesBrowser.getBrowser(), "Cannot watch: " + info.getE().getClass().getName() + "\n" + info.getE().getMessage(),
"Error", JOptionPane.ERROR_MESSAGE);
return false;
}
}
}
catch (JMSException e1)
{
JOptionPane.showMessageDialog(HermesBrowser.getBrowser(), "Cannot browse: " + e1.getClass().getName() + "\n" + e1.getMessage(), "Error",
JOptionPane.ERROR_MESSAGE);
log.error(e1.getMessage(), e1);
}
return false;
}
/**
* See if someone has double-clicked a row in the browser. If possible browse
* that queue/topic but if there is an exception cached there than show it.
*
* @param e
* @return
*/
private boolean maybeDoBrowse(MouseEvent e)
{
if (e.getClickCount() == 2)
{
if (table.getSelectedRows().length == 1)
{
return doBrowse();
}
}
return false;
}
/**
* See if we want to show a popup of options.
*
* @param e
* @return
*/
private boolean maybeDoPopup(MouseEvent e)
{
if (model.getRowCount() > 0)
{
if (table.getSelectedRows().length > 0)
{
stopItem.setEnabled(true);
stopAllItem.setEnabled(true);
browseItem.setEnabled(true);
truncateItem.setEnabled(true);
}
else
{
stopItem.setEnabled(false);
stopAllItem.setEnabled(false);
browseItem.setEnabled(false);
truncateItem.setEnabled(false);
}
getPopupMenu().show(e.getComponent(), e.getX(), e.getY());
return true;
}
return false;
}
public void doTruncate()
{
if (table.getSelectedRows().length > 0)
{
for (int i = 0; i < table.getSelectedRows().length; i++)
{
int row = table.getSelectedRows()[i];
WatchInfo info = (WatchInfo) model.getRow(table.getActualRowAt(row));
try
{
Hermes hermes = (Hermes) HermesBrowser.getBrowser().getContext().lookup(info.getHermesId());
HermesBrowser.getBrowser().getActionFactory().createTruncateAction(hermes, info.getConfig(), null);
}
catch (Throwable t)
{
log.error(t.getMessage(), t);
HermesBrowser.getBrowser().showErrorDialog("Truncating " + info.getConfig().getName(), t);
}
}
}
}
public void doStop()
{
int[] rows = table.getSelectedRows();
for (int i = 0; i < rows.length; i++)
{
rows[i] = table.getActualRowAt(rows[i]);
}
for (int i = 0; i < rows.length; i++)
{
final WatchInfo wInfo = model.getRow(rows[i]);
model.removeRow(rows[i]);
try
{
final Hermes hermes = (Hermes) HermesBrowser.getBrowser().getContext().lookup(wInfo.getHermesId());
log.debug("removing watch entry hermes=" + hermes.getId() + ", destination=" + wInfo.getConfig().getName());
watchManager.removeWatch(hermes, wInfo.getConfig().getName(), this);
}
catch (Exception e)
{
log.error(e.getMessage(), e);
}
}
}
public void doStopAll()
{
model.removeAll();
watchManager.clear();
}
public void doSave()
{
if (keepRunning)
{
try
{
updateConfig();
HermesBrowser.getBrowser().saveConfig();
Hermes.ui.getDefaultMessageSink().add("Watch config " + config.getId() + " saved.");
}
catch (HermesException e)
{
log.error(e.getMessage(), e);
JOptionPane.showConfirmDialog(HermesBrowser.getBrowser(), "Cannot save configuration: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
else
{
log.error("cannot save, keepRunning=false");
}
}
public JPopupMenu getPopupMenu()
{
if (popupMenu == null)
{
popupMenu = new JidePopupMenu();
stopItem = new JMenuItem("Remove");
stopAllItem = new JMenuItem("Remove all");
browseItem = new JMenuItem("Browse", IconCache.getIcon("hermes.browse"));
truncateItem = new JMenuItem("Truncate", IconCache.getIcon("hermes.queue.truncate"));
saveItem = new JMenuItem("Save", IconCache.getIcon("hermes.save"));
updateNow = new JMenuItem("Update", IconCache.getIcon("hermes.update"));
expandAll = new JMenuItem("Expand all", IconCache.getIcon("hermes.expand.all"));
collapseAll = new JMenuItem("Collapse All", IconCache.getIcon("hermes.collapse.all"));
popupMenu.add(stopItem);
popupMenu.add(stopAllItem);
popupMenu.add(saveItem);
popupMenu.addSeparator();
popupMenu.add(browseItem);
popupMenu.add(updateNow);
popupMenu.add(truncateItem);
popupMenu.add(expandAll);
popupMenu.add(collapseAll);
truncateItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent arg0)
{
doTruncate();
}
});
updateNow.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
watchManager.updateNow();
}
});
stopItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
doStop();
}
});
stopAllItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
doStopAll();
}
});
saveItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
doSave();
}
});
browseItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
doBrowse();
}
});
collapseAll.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent arg0)
{
table.collapseAllRows();
}
});
expandAll.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent arg0)
{
table.expandAllRows();
}
});
}
return popupMenu;
}
/**
* @return Returns the mouseListener.
*/
public MouseAdapter getMouseListener()
{
return mouseListener;
}
/**
* @return Returns the config.
*/
public WatchConfig getConfig()
{
return config;
}
/*
* (non-Javadoc)
*
* @see com.jidesoft.document.DocumentComponent#getDisplayTitle()
*/
public String getHighlighedTitle()
{
return "<html><b><font color=\"red\">" + config.getId() + "</font></b></html>";
}
public String getPlainTitle()
{
return config.getId();
}
private WatchInfo getWatchInfo(Hermes hermes, DestinationConfig dConfig) throws JMSException
{
return model.getRowByKey(new DestinationConfigKeyWrapper(dConfig));
}
public void onDepthChange(Hermes hermes, DestinationConfig dConfig, long depth)
{
try
{
WatchInfo info = getWatchInfo(hermes, dConfig);
info.setDepth((int) depth);
}
catch (JMSException e)
{
log.error(e.getMessage(), e);
}
}
public void onException(Hermes hermes, DestinationConfig dConfig, Exception e)
{
try
{
WatchInfo info = getWatchInfo(hermes, dConfig);
info.setE(e);
setTabTitle(getTitle());
repaint();
}
catch (JMSException ex)
{
log.error(ex.getMessage(), ex);
}
}
public void onOldestMessageChange(Hermes hermes, DestinationConfig dConfig, Date oldest)
{
try
{
WatchInfo info = getWatchInfo(hermes, dConfig);
if (oldest != null)
{
info.setOldest(oldest.getTime());
}
else
{
info.setOldest(0);
}
}
catch (JMSException ex)
{
log.error(ex.getMessage(), ex);
}
}
public void onPropertyChange(Hermes hermes, DestinationConfig dConfig, Map properties)
{
try
{
WatchInfo info = getWatchInfo(hermes, dConfig);
info.setStatistics(properties);
repaint();
}
catch (JMSException ex)
{
log.error(ex.getMessage(), ex);
}
}
}