001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.admin.dso;
006:
007: import org.dijon.ContainerResource;
008: import org.dijon.Spinner;
009:
010: import com.tc.admin.AdminClient;
011: import com.tc.admin.AdminClientContext;
012: import com.tc.admin.ConnectionContext;
013: import com.tc.admin.common.LockElementWrapper;
014: import com.tc.admin.common.XAbstractAction;
015: import com.tc.admin.common.XButton;
016: import com.tc.admin.common.XComboBox;
017: import com.tc.admin.common.XContainer;
018: import com.tc.admin.common.XObjectTable;
019: import com.tc.admin.common.XObjectTableModel;
020: import com.tc.admin.common.XPopupListener;
021: import com.tc.management.L2LockStatsManagerImpl.LockStackTracesStat;
022: import com.tc.management.L2LockStatsManagerImpl.LockStat;
023: import com.tc.management.beans.L2MBeanNames;
024: import com.tc.management.beans.LockStatisticsMonitorMBean;
025: import com.tc.object.lockmanager.impl.TCStackTraceElement;
026:
027: import java.awt.event.ActionEvent;
028: import java.awt.event.ActionListener;
029: import java.awt.event.KeyEvent;
030: import java.awt.event.MouseAdapter;
031: import java.awt.event.MouseEvent;
032: import java.awt.event.MouseMotionListener;
033: import java.lang.reflect.Method;
034: import java.text.ParseException;
035: import java.util.ArrayList;
036: import java.util.Collection;
037: import java.util.Collections;
038: import java.util.HashMap;
039: import java.util.Iterator;
040: import java.util.List;
041:
042: import javax.management.MBeanServerInvocationHandler;
043: import javax.swing.DefaultComboBoxModel;
044: import javax.swing.JCheckBoxMenuItem;
045: import javax.swing.JComponent;
046: import javax.swing.JMenuItem;
047: import javax.swing.JPopupMenu;
048: import javax.swing.JScrollPane;
049: import javax.swing.JSpinner;
050: import javax.swing.KeyStroke;
051: import javax.swing.ListSelectionModel;
052: import javax.swing.SpinnerNumberModel;
053: import javax.swing.event.ListSelectionEvent;
054: import javax.swing.event.ListSelectionListener;
055: import javax.swing.event.PopupMenuEvent;
056: import javax.swing.event.PopupMenuListener;
057:
058: public class LocksPanel extends XContainer implements
059: PopupMenuListener, ListSelectionListener {
060: private XComboBox m_typeCombo;
061: private Spinner m_countSpinner;
062: private Spinner m_traceDepthSpinner;
063: private Spinner m_gatherIntervalSpinner;
064: private XButton m_refreshButton;
065: private JScrollPane m_tableScroller;
066: private LockElementTable m_lockTable;
067: private LockStatTable m_lockStatTable;
068: private XObjectTableModel m_lockTableModel;
069: private LockStatisticsMonitorMBean m_lockStatsMBean;
070: private JCheckBoxMenuItem m_clientTracesEnabledToggle;
071: private ClientTracesEnabledAction m_clientTracesEnabledAction;
072: private GatherClientTracesAction m_gatherClientTracesAction;
073: private GatherAllClientTracesAction m_gatherAllClientTracesAction;
074: private ResetClientTracesAction m_resetClientTracesAction;
075: private ResetAllClientTracesAction m_resetAllClientTracesAction;
076: private XPopupListener m_popupListener;
077:
078: private final int DEFAULT_LOCK_COUNT = 100;
079: private final int DEFAULT_TRACE_DEPTH = 4;
080: private final int DEFAULT_GATHER_INTERVAL = 1;
081:
082: private final String[] ALL_TYPES = { "AggregateLockHolderStats",
083: "AggregateWaitingLocks", "Requested", "ContendedLocks",
084: "LockHops" };
085:
086: private static final String REFRESH = "Refresh";
087:
088: public LocksPanel(ConnectionContext cc) {
089: super ();
090:
091: AdminClientContext cntx = AdminClient.getContext();
092:
093: load((ContainerResource) cntx.topRes.getComponent("LocksPanel"));
094:
095: m_lockStatsMBean = (LockStatisticsMonitorMBean) MBeanServerInvocationHandler
096: .newProxyInstance(cc.mbsc,
097: L2MBeanNames.LOCK_STATISTICS,
098: LockStatisticsMonitorMBean.class, false);
099:
100: // We do this to force an early error if the server we're connecting to is old and doesn't
101: // have the LockStatsMBean. DSONode catches the error and doesn't display the LocksNode.
102: isLockStatisticsEnabled();
103:
104: m_typeCombo = (XComboBox) findComponent("TypeCombo");
105: m_typeCombo.setModel(new DefaultComboBoxModel(ALL_TYPES));
106: m_typeCombo.setSelectedIndex(0);
107: m_typeCombo.addActionListener(new ActionListener() {
108: public void actionPerformed(ActionEvent ae) {
109: setType((String) m_typeCombo.getSelectedItem());
110: }
111: });
112:
113: m_countSpinner = (Spinner) findComponent("CountSpinner");
114: m_countSpinner.setValue(Integer.valueOf(DEFAULT_LOCK_COUNT));
115: ((SpinnerNumberModel) m_countSpinner.getModel())
116: .setMinimum(Integer.valueOf(1));
117:
118: m_traceDepthSpinner = (Spinner) findComponent("TraceDepthSpinner");
119: m_traceDepthSpinner.setValue(Integer
120: .valueOf(DEFAULT_TRACE_DEPTH));
121: ((SpinnerNumberModel) m_traceDepthSpinner.getModel())
122: .setMinimum(Integer.valueOf(0));
123:
124: m_gatherIntervalSpinner = (Spinner) findComponent("GatherIntervalSpinner");
125: m_gatherIntervalSpinner.setValue(Integer
126: .valueOf(DEFAULT_GATHER_INTERVAL));
127: ((SpinnerNumberModel) m_gatherIntervalSpinner.getModel())
128: .setMinimum(Integer.valueOf(0));
129:
130: RefreshAction refreshAction = new RefreshAction();
131: m_refreshButton = (XButton) findComponent("RefreshButton");
132: m_refreshButton.addActionListener(refreshAction);
133:
134: m_popupListener = new XPopupListener();
135:
136: m_lockStatTable = new LockStatTable();
137:
138: XObjectTable table = (XObjectTable) findComponent("LockTable");
139: m_tableScroller = (JScrollPane) table
140: .getAncestorOfClass(JScrollPane.class);
141: setType((String) m_typeCombo.getSelectedItem());
142:
143: KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0, true);
144: getActionMap().put(REFRESH, refreshAction);
145: getInputMap().put(ks, REFRESH);
146:
147: JPopupMenu popup = new JPopupMenu();
148: m_clientTracesEnabledAction = new ClientTracesEnabledAction();
149: popup.add(m_clientTracesEnabledToggle = new JCheckBoxMenuItem(
150: m_clientTracesEnabledAction));
151: popup.addSeparator();
152: popup
153: .add(new JMenuItem(
154: m_gatherClientTracesAction = new GatherClientTracesAction()));
155: popup
156: .add(new JMenuItem(
157: m_gatherAllClientTracesAction = new GatherAllClientTracesAction()));
158: popup.addSeparator();
159: popup
160: .add(new JMenuItem(
161: m_resetClientTracesAction = new ResetClientTracesAction()));
162: popup
163: .add(new JMenuItem(
164: m_resetAllClientTracesAction = new ResetAllClientTracesAction()));
165: popup.addSeparator();
166: popup.add(new JMenuItem(refreshAction));
167: m_popupListener.setPopupMenu(popup);
168: popup.addPopupMenuListener(this );
169: }
170:
171: public void popupMenuCanceled(PopupMenuEvent e) {/**/
172: }
173:
174: public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {/**/
175: }
176:
177: public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
178: LockElementWrapper wrapper = m_lockTable.getSelectedWrapper();
179: if (wrapper != null) {
180: boolean clientTracesEnabled = isClientTracesEnabled(wrapper);
181: m_clientTracesEnabledToggle
182: .setSelected(clientTracesEnabled);
183: m_gatherClientTracesAction.setEnabled(clientTracesEnabled);
184: m_resetClientTracesAction.setEnabled(clientTracesEnabled);
185: }
186: }
187:
188: private void setType(String type) {
189: if (m_lockTable != null) {
190: m_lockTable.getSelectionModel()
191: .removeListSelectionListener(this );
192: m_lockTable.setSortColumn(-1);
193: }
194:
195: m_lockTableModel = new LockStatTableModel(type);
196: m_lockTable = m_lockStatTable;
197:
198: m_lockTable.setModel(m_lockTableModel);
199: m_popupListener.setTarget(m_lockTable);
200: m_tableScroller.setViewportView(m_lockTable);
201: m_lockTable.getSelectionModel().addListSelectionListener(this );
202: }
203:
204: private Object getSpinnerValue(JSpinner spinner) {
205: try {
206: spinner.commitEdit();
207: } catch (ParseException pe) {
208: // Edited value is invalid, spinner.getValue() will return
209: // the last valid value, you could revert the spinner to show that:
210: JComponent editor = spinner.getEditor();
211: if (editor instanceof JSpinner.DefaultEditor) {
212: ((JSpinner.DefaultEditor) editor).getTextField()
213: .setValue(spinner.getValue());
214: }
215: }
216: return spinner.getValue();
217: }
218:
219: private int getMaxLocks() {
220: Object o = getSpinnerValue(m_countSpinner);
221: if (o instanceof Integer) {
222: return ((Integer) o).intValue();
223: }
224: return DEFAULT_LOCK_COUNT;
225: }
226:
227: private int getTraceDepth() {
228: Object o = getSpinnerValue(m_traceDepthSpinner);
229: if (o instanceof Integer) {
230: return ((Integer) o).intValue();
231: }
232: return DEFAULT_TRACE_DEPTH;
233: }
234:
235: private int getGatherInterval() {
236: Object o = getSpinnerValue(m_gatherIntervalSpinner);
237: if (o instanceof Integer) {
238: return ((Integer) o).intValue();
239: }
240: return DEFAULT_GATHER_INTERVAL;
241: }
242:
243: public class RefreshAction extends XAbstractAction {
244: RefreshAction() {
245: super ("Refresh");
246: }
247:
248: public void actionPerformed(ActionEvent ae) {
249: refresh();
250: }
251: }
252:
253: public void setLockStatisticsEnabled(boolean lockStatsEnabled) {
254: m_lockStatsMBean.setLockStatisticsEnabled(lockStatsEnabled);
255: refresh();
256: }
257:
258: public boolean isLockStatisticsEnabled() {
259: return m_lockStatsMBean.isLockStatisticsEnabled();
260: }
261:
262: public class ClientTracesEnabledAction extends XAbstractAction {
263: ClientTracesEnabledAction() {
264: super ("Client traces enabled");
265: }
266:
267: public void actionPerformed(ActionEvent ae) {
268: LockElementWrapper wrapper = m_lockTable
269: .getSelectedWrapper();
270: if (wrapper != null) {
271: setClientTracesEnabled(wrapper,
272: m_clientTracesEnabledToggle.isSelected());
273: }
274: }
275: }
276:
277: private boolean isClientTracesEnabled(LockElementWrapper wrapper) {
278: return m_lockStatsMBean.isClientStackTraceEnabled(wrapper
279: .getLockID());
280: }
281:
282: private void setClientTracesEnabled(LockElementWrapper wrapper,
283: boolean clientTracesEnabled) {
284: String lockId = wrapper.getLockID();
285: if (clientTracesEnabled) {
286: m_lockStatsMBean.enableClientStackTrace(lockId,
287: getTraceDepth(), getGatherInterval());
288: } else {
289: m_lockStatsMBean.disableClientStackTrace(lockId);
290: wrapper.setStackTrace(null);
291: }
292: }
293:
294: public void setAllClientTracesEnabled(boolean allStatsEnabled) {
295: int count = m_lockTableModel.getRowCount();
296: for (int i = 0; i < count; i++) {
297: LockElementWrapper wrapper = m_lockTable.getWrapperAt(i);
298: setClientTracesEnabled(wrapper, allStatsEnabled);
299: }
300: }
301:
302: public class GatherClientTracesAction extends XAbstractAction {
303: GatherClientTracesAction() {
304: super ("Gather client traces");
305: }
306:
307: public void actionPerformed(ActionEvent ae) {
308: LockElementWrapper wrapper = m_lockTable
309: .getSelectedWrapper();
310: if (wrapper != null) {
311: gatherTrace(wrapper, true);
312: }
313: }
314: }
315:
316: public class ResetClientTracesAction extends XAbstractAction {
317: ResetClientTracesAction() {
318: super ("Reset client traces");
319: }
320:
321: public void actionPerformed(ActionEvent ae) {
322: LockElementWrapper wrapper = m_lockTable
323: .getSelectedWrapper();
324: if (wrapper != null) {
325: setClientTracesEnabled(wrapper, false);
326: setClientTracesEnabled(wrapper, true);
327: }
328: }
329: }
330:
331: public class ResetAllClientTracesAction extends XAbstractAction {
332: ResetAllClientTracesAction() {
333: super ("Reset all client traces");
334: }
335:
336: public void actionPerformed(ActionEvent ae) {
337: LockElementWrapper wrapper = m_lockTable
338: .getSelectedWrapper();
339: if (wrapper != null) {
340: setAllClientTracesEnabled(false);
341: setAllClientTracesEnabled(true);
342: }
343: }
344: }
345:
346: private void gatherTrace(LockElementWrapper wrapper,
347: boolean toConsole) {
348: String id = wrapper.getLockID();
349: Collection<LockStackTracesStat> c = m_lockStatsMBean
350: .getStackTraces(id);
351: if (c.isEmpty()) {
352: return;
353: }
354: Iterator<LockStackTracesStat> iter = c.iterator();
355: HashMap<TCStackTraceElement, TCStackTraceElement> map = new HashMap<TCStackTraceElement, TCStackTraceElement>();
356: while (iter.hasNext()) {
357: LockStackTracesStat sts = iter.next();
358: List<TCStackTraceElement> stackTraces = sts
359: .getStackTraces();
360: if (!stackTraces.isEmpty()) {
361: for (TCStackTraceElement elem : stackTraces) {
362: map.put(elem, elem);
363: }
364: }
365: }
366: Iterator<TCStackTraceElement> i = map.keySet().iterator();
367: StringBuffer allTraces = new StringBuffer();
368: StringBuffer sb = new StringBuffer("<html>");
369: while (i.hasNext()) {
370: TCStackTraceElement elem = i.next();
371: for (StackTraceElement ste : elem.getStackTraceElements()) {
372: String s = ste.toString();
373: sb.append("<p>");
374: sb.append(s);
375: sb.append("</p>\n");
376:
377: allTraces.append(s);
378: allTraces.append("\n");
379: }
380: if (i.hasNext()) {
381: sb.append("<br>");
382: allTraces.append("\n");
383: }
384: }
385: sb.append("</html>");
386: wrapper.setStackTrace(sb.toString());
387:
388: String allStackTraces = allTraces.toString();
389: if (toConsole)
390: AdminClient.getContext().log(allStackTraces);
391: wrapper.setAllStackTraces(allStackTraces);
392: }
393:
394: public class GatherAllClientTracesAction extends XAbstractAction {
395: GatherAllClientTracesAction() {
396: super ("Gather all client traces");
397: }
398:
399: public void actionPerformed(ActionEvent ae) {
400: int count = m_lockTableModel.getRowCount();
401: for (int i = 0; i < count; i++) {
402: LockElementWrapper wrapper = m_lockTable
403: .getWrapperAt(i);
404: gatherTrace(wrapper, false);
405: }
406: }
407: }
408:
409: private void updateTableModel() {
410: setType((String) m_typeCombo.getSelectedItem());
411: }
412:
413: public void refresh() {
414: AdminClientContext acc = AdminClient.getContext();
415:
416: acc.controller
417: .setStatus(acc.getMessage("dso.locks.refreshing"));
418: updateTableModel();
419: acc.controller.clearStatus();
420: }
421:
422: class LockElementTable extends XObjectTable implements
423: MouseMotionListener {
424: LockElementTable() {
425: super ();
426: setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
427: addMouseMotionListener(this );
428: addMouseListener(new MouseAdapter() {
429: public void mousePressed(MouseEvent e) {
430: int row = rowAtPoint(e.getPoint());
431: setRowSelectionInterval(row, row);
432: }
433: });
434: }
435:
436: LockElementWrapper getSelectedWrapper() {
437: int row = getSelectedRow();
438: return (row != -1) ? getWrapperAt(row) : null;
439: }
440:
441: LockElementWrapper getWrapperAt(int i) {
442: return (LockElementWrapper) m_lockTableModel.getObjectAt(i);
443: }
444:
445: public void mouseDragged(MouseEvent e) {/**/
446: }
447:
448: public void mouseMoved(MouseEvent e) {
449: LockElementWrapper wrapper = getWrapperAt(rowAtPoint(e
450: .getPoint()));
451: String tip = null;
452: if (wrapper != null) {
453: tip = wrapper.getStackTrace();
454: }
455: setToolTipText(tip);
456: }
457: }
458:
459: static final String[] LOCK_STAT_ATTRS = { "LockID",
460: "NumOfLockRequested", "NumOfLockReleased",
461: "NumOfPendingRequests", "NumOfPendingWaiters",
462: "NumOfLockHopRequests", "AvgWaitTimeInMillis",
463: "AvgHeldTimeInMillis" };
464:
465: static final String[] LOCK_STAT_COLS = { "LockID", "LockRequested",
466: "LockReleased", "PendingRequests", "PendingWaiters",
467: "LockHopRequests", "AvgWaitMillis", "AvgHeldMillis" };
468:
469: class LockStatTableModel extends XObjectTableModel {
470: String lockType;
471:
472: LockStatTableModel(String lockType) {
473: super (LockStatWrapper.class, LOCK_STAT_ATTRS,
474: LOCK_STAT_COLS);
475: this .lockType = lockType;
476: init();
477: }
478:
479: private Collection getCollection() {
480: try {
481: Method m = m_lockStatsMBean.getClass().getMethod(
482: "getTop" + lockType,
483: new Class[] { Integer.TYPE });
484: return (Collection) m
485: .invoke(m_lockStatsMBean,
486: new Object[] { Integer
487: .valueOf(getMaxLocks()) });
488: } catch (Exception e) {
489: e.printStackTrace();
490: }
491: return Collections.EMPTY_LIST;
492: }
493:
494: private void init() {
495: try {
496: set(wrap(getCollection()));
497: } catch (Throwable t) {
498: t.printStackTrace();
499: }
500: }
501:
502: Object[] wrap(Collection c) {
503: Iterator i = c.iterator();
504: ArrayList<LockStatWrapper> l = new ArrayList<LockStatWrapper>();
505: while (i.hasNext()) {
506: l.add(new LockStatWrapper((LockStat) i.next()));
507: }
508: return l.toArray(new LockStatWrapper[0]);
509: }
510: }
511:
512: class LockStatTable extends LockElementTable {
513: LockStatTable() {
514: super ();
515: }
516: }
517:
518: public void valueChanged(ListSelectionEvent e) {
519: if (e.getValueIsAdjusting()) {
520: return;
521: }
522:
523: LockElementWrapper wrapper = m_lockTable.getSelectedWrapper();
524: if (wrapper != null) {
525: String allTraces = wrapper.getAllStackTraces();
526: if (allTraces != null) {
527: AdminClient.getContext().log(allTraces);
528: }
529: }
530: }
531: }
|