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
038public class TMMonitor extends JPanel
039        implements TransactionStatusListener, SwingConstants, ActionListener, AncestorListener
040{
041    UI ui;
042    TransactionManager txnmgr;
043    JTable table;
044    AbstractTableModel model;
045    TransactionStatusEvent[] events;
046    JLabel tps = new JLabel("0");
047    JLabel tpsAvg = new JLabel("0.00");
048    JLabel tpsPeak = new JLabel("0");
049    JLabel inTransit = new JLabel("0");
050    JLabel outstanding = new JLabel("0");
051    Timer timer;
052
053    static final Font SMALL  = Font.decode ("terminal-plain-8");
054    static final Font LARGE  = Font.decode ("terminal-plain-18");
055
056    Color[] color = new Color[] {
057        /* READY               */ Color.white,
058        /* PREPARING           */ new Color (0xb3, 0xb3, 0xff), // blue
059        /* PREPARING_FOR_ABORT */ new Color (0xff, 0xa3, 0xa3), // red
060        /* COMMITTING          */ new Color (0xd1, 0xff, 0xd1), // green
061        /* ABORTING            */ new Color (0xff, 0xa3, 0xa3), // red
062        /* DONE                */ Color.white,
063        /* PAUSED              */ new Color (0xff, 0x7f, 0x50)  // orange
064    };
065
066    public TMMonitor (UI ui, TransactionManager tmmgr) {
067        super();
068        this.ui = ui;
069        this.txnmgr = tmmgr;
070        setLayout(new BorderLayout());
071        setBorder(BorderFactory.createRaisedBevelBorder());
072        model = createModel ();
073
074        table = createTable (model);
075        JScrollPane scrollPane = new JScrollPane(table);
076
077        add(createTPSPanel(), BorderLayout.EAST);
078        add(scrollPane, BorderLayout.CENTER);
079        tmmgr.addListener(this);
080        addAncestorListener(this);
081    }
082    public void update(TransactionStatusEvent e) {
083        if (ui.isDestroyed()) {
084            return;
085        }
086        int row = e.getSession();
087        events[row] = e;
088        model.fireTableRowsUpdated(row, row);
089        // table.getSelectionModel().setSelectionInterval(row, row);
090        setBackgroundColor (row, color[e.getState().intValue()]);
091        inTransit.setText (Long.toString (txnmgr.getInTransit()));
092        outstanding.setText (Long.toString (txnmgr.getOutstandingTransactions()));
093    }
094    private void setBackgroundColor (int row, Color color) {
095        for (int i=0; i<model.getColumnCount(); i++) {
096            ((DefaultTableCellRenderer)table.getCellRenderer(row, i)).setBackground(color);
097        }
098    }
099    private JTable createTable (TableModel model) {
100        JTable table = new JTable (model);
101        table.setSurrendersFocusOnKeystroke(true);
102        table.setFillsViewportHeight(true);
103        table.setShowVerticalLines(true);
104        table.setCellSelectionEnabled(false);
105        table.setDoubleBuffered(true);
106        TableColumnModel tcm = table.getColumnModel();
107
108        tcm.getColumn(0).setPreferredWidth(10);
109        tcm.getColumn(1).setPreferredWidth(25);
110        tcm.getColumn(2).setPreferredWidth(100);
111        tcm.getColumn(3).setPreferredWidth(600);
112        return table;
113    }
114    private JComponent createTPSPanel () {
115        JPanel outer = new JPanel (new BorderLayout());
116
117        JPanel p = new JPanel (new GridLayout (5,2));
118
119        add (p, tps, "TPS");
120        add (p, tpsPeak, "Peak TPS");
121        add (p, tpsAvg, "Avg TPS");
122        add (p, inTransit, "InTransit");
123        add (p, outstanding, "Queue");
124
125        JPanel blackFiller = new JPanel();
126        outer.add (p, BorderLayout.NORTH);
127        blackFiller.setBackground(Color.black);
128        outer.add (blackFiller, BorderLayout.CENTER);
129        outer.setPreferredSize(new Dimension (170, 0));
130        return outer;
131    }
132
133    private void add (JPanel p, JLabel l, String description) {
134        setLabelStyle (l, LARGE, RIGHT);
135        p.add (l);
136        p.add (setLabelStyle (new JLabel (description), SMALL, LEFT));
137    }
138    private JLabel setLabelStyle (JLabel l, Font f, int alignment) {
139        l.setBorder (BorderFactory.createLoweredBevelBorder ());
140        l.setFont (f);
141        l.setOpaque (true);
142        l.setForeground(Color.green);
143        l.setBackground(Color.black);
144        // l.setAlignment(alignment);
145        l.setHorizontalAlignment(alignment);
146        l.setVerticalAlignment(BOTTOM);
147        return l;
148    }
149    private AbstractTableModel createModel () {
150        events = new TransactionStatusEvent[txnmgr.getActiveSessions()];
151        return new AbstractTableModel() {
152            String[] columnName = new String[] {
153               "#", "id", "State", "Info"
154            };
155            Class[] columnClass = new Class[] {
156                Integer.class, Long.class, String.class, String.class
157            };
158            public int getColumnCount() {
159                return 4;
160            }
161            public int getRowCount() {
162                return txnmgr.getActiveSessions();
163            }
164            @Override
165            public String getColumnName(int columnIndex) {
166                return columnName[columnIndex];
167            }
168            public Class getColumnClass(int columnIndex) {
169                return columnClass[columnIndex];
170            }
171            public Object getValueAt(int row, int col) {
172                if (events[row] != null) {
173                    switch (col) {
174                        case 0:
175                            return row;
176                        case 1:
177                            return events[row].getId();
178                        case 2:
179                            return events[row].getStateAsString();
180                        case 3:
181                            return events[row].getInfo();
182                    }
183                }
184                return "";
185            }
186        };
187    }
188
189    public void actionPerformed(ActionEvent e) {
190        TPS t = txnmgr.getTPS();
191        tps.setText (Integer.toString (t.intValue()));
192        tpsAvg.setText (String.format ("%.2f", t.getAvg()));
193        tpsPeak.setText (Integer.toString (t.getPeak()));
194    }
195
196    public void ancestorAdded(AncestorEvent event) {
197        if (timer == null) {
198            timer = new Timer (1000, this);
199            timer.start();
200        }
201    }
202
203    public void ancestorRemoved(AncestorEvent event) {
204        if (timer != null) {
205            timer.stop();
206            timer = null;
207        }
208    }
209
210    public void ancestorMoved(AncestorEvent event) { }
211}