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.iso.gui;
020
021/**
022 * allows for easy visualization of channel utilization. 
023 * It shows messages coming through in an
024 * 'Oscilloscope' style clickeable window.
025 * @see ISOMeter
026 * @see ISOMsgPanel
027 * @serial
028 */
029
030import org.jpos.iso.*;
031
032import javax.swing.*;
033import java.awt.*;
034import java.util.Observable;
035import java.util.Observer;
036
037/** Swing panel that displays ISO channel activity and recent log entries. */
038public class ISOChannelPanel extends JPanel implements Observer {
039    private static final long serialVersionUID = -8069489863639386589L;
040    /**
041     * Meter widget used to visualize channel activity.
042     *
043     * @serial embedded child component state
044     */
045    ISOMeter meter;
046    /**
047     * Rolling log model displayed when the meter is opened.
048     *
049     * @serial serialized log contents
050     */
051    DefaultListModel log;
052    /**
053     * Human-readable channel name shown in the UI.
054     *
055     * @serial display name persisted with the panel
056     */
057    String symbolicName;
058
059    /** Fields that must be protected before logging. */
060    private int[] protectFields = null;
061    /** Fields that must be wiped before logging. */
062    private int[] wipeFields    = null;
063
064    /** Maximum number of log entries to retain. */
065    public static final int LOG_CAPACITY = 250;
066
067    /**
068     * Constructs a panel for the given channel.
069     * @param channel      the ISO channel
070     * @param symbolicName the display name
071     */
072    public ISOChannelPanel (ISOChannel channel, String symbolicName)
073    {
074        super();
075        this.symbolicName = symbolicName;
076        setLayout(new FlowLayout());
077        setBorder(BorderFactory.createRaisedBevelBorder());
078        log = new DefaultListModel();
079        add(createCountersPanel());
080        meter.setConnected(channel.isConnected());
081        if (channel instanceof Observable) 
082            ((Observable)channel).addObserver(this);
083    }
084    /**
085     * Unconnected constructor allows for instantiation of
086     * ISOChannelPanel without associated ISOChannel
087     * (that can be attached later)
088     * @param symbolicName the display name
089     */
090    public ISOChannelPanel (String symbolicName) {
091        super();
092        this.symbolicName = symbolicName;
093        setLayout(new FlowLayout());
094        setBorder(BorderFactory.createRaisedBevelBorder());
095        log = new DefaultListModel();
096        add(createCountersPanel());
097        meter.setConnected(false);
098    }
099
100    /** Returns the symbolic name.
101     * @return symbolic name
102     */
103    public final String getSymbolicName() {
104        return symbolicName;
105    }
106    /** Returns the log model.
107     * @return the log model
108     */
109    public final ListModel getLog() {
110        return log;
111    }
112
113    public void update(Observable o, Object arg) {
114        if (arg != null && arg instanceof ISOMsg) {
115            ISOMsg m = (ISOMsg) arg;
116            try {
117                String mti = (String) m.getValue(0);
118                int imti   = Integer.parseInt(mti);
119                if (m.isIncoming())
120                    meter.setValue(-imti, mti);
121                else 
122                    meter.setValue(imti, mti);
123
124                // log.insertElementAt(m,0);
125                log.addElement(getProtectedClone (m));
126                if (log.getSize() > LOG_CAPACITY) 
127                    log.remove(0);
128
129            } catch (ISOException e) { 
130                meter.setValue(ISOMeter.mass, "ERROR");
131            }
132            meter.setValue(ISOMeter.mass);
133        }
134        if (o instanceof BaseChannel) {
135            BaseChannel c = (BaseChannel) o;
136            meter.setConnected(c.isConnected());
137            int cnt[] = c.getCounters();
138            try {
139                meter.setPositiveCounter(
140                    ISOUtil.zeropad(Integer.toString(cnt[ISOChannel.TX % 1000000000]), 9)
141                );
142                meter.setNegativeCounter(
143                    ISOUtil.zeropad(Integer.toString(cnt[ISOChannel.RX] % 1000000000), 9)
144                );
145            } catch (ISOException e) { }
146        } else if (o instanceof ISOServer) {
147            final ISOServer server = (ISOServer) o;
148            final Runnable updateIt = new Runnable() {
149                public void run() {
150                    ISOUtil.sleep (250L);
151                    int active = server.getActiveConnections();
152                    meter.setConnected(active > 0);
153                    try {
154                        meter.setPositiveCounter(
155                            ISOUtil.zeropad(Integer.toString(active), 6)
156                        );
157                    } catch (ISOException e) { }
158                    meter.repaint();
159                }
160            };
161            SwingUtilities.invokeLater (updateIt);
162        } else 
163            meter.setConnected(true);
164    }
165    /** Returns the ISO meter component.
166     * @return the ISOMeter
167     */
168    public ISOMeter getISOMeter() {
169        return meter;
170    }
171    /** Sets the fields to protect in output.
172     * @param fields field numbers to protect
173     */
174    public void setProtectFields (int[] fields) {
175        protectFields = fields;
176    }
177    /** Sets the fields to wipe in output.
178     * @param fields field numbers to wipe
179     */
180    public void setWipeFields (int[] fields) {
181        wipeFields    = fields;
182    }
183    private JPanel createCountersPanel() {
184        JPanel A = new JPanel() {
185
186            private static final long serialVersionUID = 1175437215105556679L;
187
188            public Insets getInsets() {
189                return new Insets(10,10,10,10);
190            }
191        };
192
193        A.setLayout(new BorderLayout());
194
195        meter = new ISOMeter(this);
196
197        JLabel l = new JLabel(symbolicName);
198        A.add(l, BorderLayout.NORTH);
199        A.add(meter, BorderLayout.CENTER);
200        // meter.start(); -- ISOMeter has auto-start now
201        return A;
202    }
203    private ISOMsg getProtectedClone (ISOMsg m) throws ISOException {
204        ISOMsg pm = (ISOMsg) m.clone ();
205        if (protectFields != null)
206            checkProtected(pm);
207        if (wipeFields != null)
208            checkHidden (pm);
209        return pm;
210    }
211    private void checkProtected (ISOMsg m) throws ISOException {
212        for (int f : protectFields) {
213            if (m.hasField(f))
214                m.set(f, ISOUtil.protect(m.getString(f)));
215        }
216    }
217    private void checkHidden (ISOMsg m) throws ISOException {
218        for (int f : wipeFields) {
219            if (m.hasField(f))
220                m.set(f, "[WIPED]");
221        }
222    }
223}