001/* 002 * jPOS Project [http://jpos.org] 003 * Copyright (C) 2000-2026 jPOS Software SRL 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.jpos.transaction.gui; 020 021import org.jpos.transaction.TransactionStatusListener; 022import org.jpos.transaction.TransactionStatusEvent; 023import org.jpos.transaction.TransactionManager; 024import org.jpos.ui.UI; 025import org.jpos.util.TPS; 026 027import javax.swing.*; 028import javax.swing.event.AncestorEvent; 029import javax.swing.event.AncestorListener; 030import javax.swing.table.TableModel; 031import javax.swing.table.AbstractTableModel; 032import javax.swing.table.TableColumnModel; 033import javax.swing.table.DefaultTableCellRenderer; 034import java.awt.*; 035import java.awt.event.ActionEvent; 036import java.awt.event.ActionListener; 037 038/** 039 * Swing panel that displays transaction-manager activity in real time. 040 */ 041public class TMMonitor extends JPanel 042 implements TransactionStatusListener, SwingConstants, ActionListener, AncestorListener 043{ 044 /** UI container that owns this monitor. */ 045 UI ui; 046 /** Transaction manager being monitored. */ 047 TransactionManager txnmgr; 048 /** Table used to render session rows. */ 049 JTable table; 050 /** Backing table model for the session view. */ 051 AbstractTableModel model; 052 /** Latest event observed for each transaction-manager session. */ 053 TransactionStatusEvent[] events; 054 /** Current transactions-per-second indicator. */ 055 JLabel tps = new JLabel("0"); 056 /** Average transactions-per-second indicator. */ 057 JLabel tpsAvg = new JLabel("0.00"); 058 /** Peak transactions-per-second indicator. */ 059 JLabel tpsPeak = new JLabel("0"); 060 /** Current in-transit transaction counter. */ 061 JLabel inTransit = new JLabel("0"); 062 /** Current outstanding-transaction counter. */ 063 JLabel outstanding = new JLabel("0"); 064 /** Periodic timer used to refresh TPS counters. */ 065 Timer timer; 066 067 static final Font SMALL = Font.decode ("terminal-plain-8"); 068 static final Font LARGE = Font.decode ("terminal-plain-18"); 069 070 /** Background color palette indexed by transaction state. */ 071 Color[] color = new Color[] { 072 /* READY */ Color.white, 073 /* PREPARING */ new Color (0xb3, 0xb3, 0xff), // blue 074 /* PREPARING_FOR_ABORT */ new Color (0xff, 0xa3, 0xa3), // red 075 /* COMMITTING */ new Color (0xd1, 0xff, 0xd1), // green 076 /* ABORTING */ new Color (0xff, 0xa3, 0xa3), // red 077 /* DONE */ Color.white, 078 /* PAUSED */ new Color (0xff, 0x7f, 0x50) // orange 079 }; 080 081 /** 082 * Constructs a Swing monitor for the given transaction manager. 083 * 084 * @param ui parent UI used for redraw scheduling 085 * @param tmmgr transaction manager whose sessions are displayed 086 */ 087 public TMMonitor (UI ui, TransactionManager tmmgr) { 088 super(); 089 this.ui = ui; 090 this.txnmgr = tmmgr; 091 setLayout(new BorderLayout()); 092 setBorder(BorderFactory.createRaisedBevelBorder()); 093 model = createModel (); 094 095 table = createTable (model); 096 JScrollPane scrollPane = new JScrollPane(table); 097 098 add(createTPSPanel(), BorderLayout.EAST); 099 add(scrollPane, BorderLayout.CENTER); 100 tmmgr.addListener(this); 101 addAncestorListener(this); 102 } 103 public void update(TransactionStatusEvent e) { 104 if (ui.isDestroyed()) { 105 return; 106 } 107 int row = e.getSession(); 108 events[row] = e; 109 model.fireTableRowsUpdated(row, row); 110 // table.getSelectionModel().setSelectionInterval(row, row); 111 setBackgroundColor (row, color[e.getState().intValue()]); 112 inTransit.setText (Long.toString (txnmgr.getInTransit())); 113 outstanding.setText (Long.toString (txnmgr.getOutstandingTransactions())); 114 } 115 private void setBackgroundColor (int row, Color color) { 116 for (int i=0; i<model.getColumnCount(); i++) { 117 ((DefaultTableCellRenderer)table.getCellRenderer(row, i)).setBackground(color); 118 } 119 } 120 private JTable createTable (TableModel model) { 121 JTable table = new JTable (model); 122 table.setSurrendersFocusOnKeystroke(true); 123 table.setFillsViewportHeight(true); 124 table.setShowVerticalLines(true); 125 table.setCellSelectionEnabled(false); 126 table.setDoubleBuffered(true); 127 TableColumnModel tcm = table.getColumnModel(); 128 129 tcm.getColumn(0).setPreferredWidth(10); 130 tcm.getColumn(1).setPreferredWidth(25); 131 tcm.getColumn(2).setPreferredWidth(100); 132 tcm.getColumn(3).setPreferredWidth(600); 133 return table; 134 } 135 private JComponent createTPSPanel () { 136 JPanel outer = new JPanel (new BorderLayout()); 137 138 JPanel p = new JPanel (new GridLayout (5,2)); 139 140 add (p, tps, "TPS"); 141 add (p, tpsPeak, "Peak TPS"); 142 add (p, tpsAvg, "Avg TPS"); 143 add (p, inTransit, "InTransit"); 144 add (p, outstanding, "Queue"); 145 146 JPanel blackFiller = new JPanel(); 147 outer.add (p, BorderLayout.NORTH); 148 blackFiller.setBackground(Color.black); 149 outer.add (blackFiller, BorderLayout.CENTER); 150 outer.setPreferredSize(new Dimension (170, 0)); 151 return outer; 152 } 153 154 private void add (JPanel p, JLabel l, String description) { 155 setLabelStyle (l, LARGE, RIGHT); 156 p.add (l); 157 p.add (setLabelStyle (new JLabel (description), SMALL, LEFT)); 158 } 159 private JLabel setLabelStyle (JLabel l, Font f, int alignment) { 160 l.setBorder (BorderFactory.createLoweredBevelBorder ()); 161 l.setFont (f); 162 l.setOpaque (true); 163 l.setForeground(Color.green); 164 l.setBackground(Color.black); 165 // l.setAlignment(alignment); 166 l.setHorizontalAlignment(alignment); 167 l.setVerticalAlignment(BOTTOM); 168 return l; 169 } 170 private AbstractTableModel createModel () { 171 events = new TransactionStatusEvent[txnmgr.getActiveSessions()]; 172 return new AbstractTableModel() { 173 String[] columnName = new String[] { 174 "#", "id", "State", "Info" 175 }; 176 Class[] columnClass = new Class[] { 177 Integer.class, Long.class, String.class, String.class 178 }; 179 public int getColumnCount() { 180 return 4; 181 } 182 public int getRowCount() { 183 return txnmgr.getActiveSessions(); 184 } 185 @Override 186 public String getColumnName(int columnIndex) { 187 return columnName[columnIndex]; 188 } 189 public Class getColumnClass(int columnIndex) { 190 return columnClass[columnIndex]; 191 } 192 public Object getValueAt(int row, int col) { 193 if (events[row] != null) { 194 switch (col) { 195 case 0: 196 return row; 197 case 1: 198 return events[row].getId(); 199 case 2: 200 return events[row].getStateAsString(); 201 case 3: 202 return events[row].getInfo(); 203 } 204 } 205 return ""; 206 } 207 }; 208 } 209 210 public void actionPerformed(ActionEvent e) { 211 TPS t = txnmgr.getTPS(); 212 tps.setText (Integer.toString (t.intValue())); 213 tpsAvg.setText (String.format ("%.2f", t.getAvg())); 214 tpsPeak.setText (Integer.toString (t.getPeak())); 215 } 216 217 public void ancestorAdded(AncestorEvent event) { 218 if (timer == null) { 219 timer = new Timer (1000, this); 220 timer.start(); 221 } 222 } 223 224 public void ancestorRemoved(AncestorEvent event) { 225 if (timer != null) { 226 timer.stop(); 227 timer = null; 228 } 229 } 230 231 public void ancestorMoved(AncestorEvent event) { } 232}