org.qedeq.gui.se.pane.ProcessListPane.java Source code

Java tutorial

Introduction

Here is the source code for org.qedeq.gui.se.pane.ProcessListPane.java

Source

/* This file is part of the project "Hilbert II" - http://www.qedeq.org
 *
 * Copyright 2000-2013,  Michael Meyling <mime@qedeq.org>.
 *
 * "Hilbert II" is free software; you can redistribute
 * it and/or modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 */

package org.qedeq.gui.se.pane;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;

import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumnModel;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;

import org.qedeq.base.io.IoUtility;
import org.qedeq.base.trace.Trace;
import org.qedeq.base.utility.DateUtility;
import org.qedeq.base.utility.YodaUtility;
import org.qedeq.gui.se.util.GuiHelper;
import org.qedeq.kernel.bo.common.QedeqBo;
import org.qedeq.kernel.bo.common.QedeqBoSet;
import org.qedeq.kernel.bo.common.ServiceCall;
import org.qedeq.kernel.bo.common.ServiceProcess;

import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.forms.layout.RowSpec;

/**
 * Shows QEDEQ module specific error pane.
 *
 * @author  Michael Meyling
 */

public class ProcessListPane extends JPanel {

    /** This class. */
    private static final Class CLASS = ProcessListPane.class;

    /** Currently selected process. */
    private final Set selectedProcesses;

    /** Start automatic refresh thread? */
    private boolean automaticRefresh = true;

    /** Table model. */
    private ProcessListModel model = new ProcessListModel();

    /** This table holds the error descriptions. */
    private final JTable list = new JTable(model) {

        /**
         * Just return the service process of the row.
         *
         * @param  e   Mouse event.
         * @return Tool tip text.
         */
        public String getToolTipText(final MouseEvent e) {
            String tip = null;
            java.awt.Point p = e.getPoint();
            final int row = rowAtPoint(p);
            final int col = columnAtPoint(p);
            try {
                final ServiceProcess process = model.getServiceProcess(row);
                switch (col) {
                case 0:
                    if (process.isBlocked()) {
                        tip = "Process is waiting";
                    } else if (process.isRunning()) {
                        tip = "Process is running";
                    } else if (process.wasFailure()) {
                        tip = "Process was stopped.";
                    } else if (process.wasSuccess()) {
                        tip = "Process has finished";
                    }
                    break;
                case 1:
                    tip = (process.getServiceCall() != null
                            ? process.getServiceCall().getService().getServiceDescription()
                            : process.getExecutionActionDescription());
                    break;
                case 2:
                case 3:
                case 4:
                case 5:
                    tip = getUrlList(process.getCurrentlyProcessedModules());
                    break;
                case 7:
                    tip = GuiHelper.getToolTipText(process.getExecutionActionDescription());
                    break;
                case 8:
                    tip = GuiHelper.getToolTipText(
                            process.getServiceCall() != null
                                    ? process.getServiceCall().getParametersString() + "; "
                                            + process.getServiceCall().getParametersString()
                                    : "");
                    break;
                default:
                    tip = "";
                }
            } catch (RuntimeException ex) {
                return super.getToolTipText(e);
            }
            return tip;
        }

    };

    private String getUrlList(final QedeqBo[] list) {
        final StringBuffer result = new StringBuffer();
        for (int i = 0; i < list.length; i++) {
            if (i > 0) {
                result.append(", ");
            }
            result.append(list[i].getUrl());
        }
        return result.toString();
    }

    /** Write with this font attributes. */
    private final SimpleAttributeSet errorAttrs = new SimpleAttributeSet();

    /** Our scroll area. */
    private JScrollPane scrollPane;

    /**
     * Creates new panel.
     */
    public ProcessListPane() {
        super(false);
        selectedProcesses = new TreeSet();
        setupView();
    }

    /**
     * Send selection events.
     */
    //    private void selectLine() {
    //        Trace.param(CLASS, this, "selectLine", "selectedLine", selectedLine);
    //    }

    /**
     * Assembles the GUI components of the panel.
     */
    private final void setupView() {
        list.setFont(new Font("Lucida Sans Unicode", Font.PLAIN, list.getFont().getSize()));
        list.addComponentListener(new ComponentAdapter() {
            public void componentResized(final ComponentEvent e) {
                list.scrollRectToVisible(list.getCellRect(list.getRowCount() - 1, 0, true));
            }
        });

        final FormLayout layout = new FormLayout("min:grow", "0:grow");
        final DefaultFormBuilder builder = new DefaultFormBuilder(layout, this);
        builder.setBorder(BorderFactory.createEmptyBorder());
        builder.setRowGroupingEnabled(true);

        final CellConstraints cc = new CellConstraints();
        builder.appendRow(new RowSpec("0:grow"));

        list.setDefaultRenderer(Icon.class, new IconCellRenderer());

        final ListSelectionModel rowSM = list.getSelectionModel();
        rowSM.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);

        // if selection changes remember the process number (= selected row (starting with 0))
        rowSM.addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(final ListSelectionEvent e) {
                if (e.getValueIsAdjusting()) {
                    return;
                }
                selectedProcesses.clear();
                if (!rowSM.isSelectionEmpty()) {
                    int minIndex = rowSM.getMinSelectionIndex();
                    int maxIndex = rowSM.getMaxSelectionIndex();
                    for (int i = minIndex; i <= maxIndex; i++) {
                        if (rowSM.isSelectedIndex(i)) {
                            final ServiceProcess process = model.getServiceProcess(i);
                            if (process != null) {
                                selectedProcesses.add(process);
                            }
                        }
                    }
                }
            }
        });
        /*
                // doing a click shall open the edit window
                list.addMouseListener(new MouseAdapter()  {
        public void mouseClicked(final MouseEvent e) {
            if (e.getClickCount() == 2) {
                Trace.trace(CLASS, this, "setupView$vmouseClicked", "doubleClick");
            }
            selectLine();
        }
                });
            
                // pressing the enter key shall open the edit window
                list.getActionMap().put("selectNextRowCell", new AbstractAction() {
        public void actionPerformed(final ActionEvent event) {
            Trace.param(CLASS, this, "setupView$actionPerformed", "event" , event);
            selectLine();
        }
                });
        */
        scrollPane = new JScrollPane(list);
        builder.add(scrollPane, cc.xywh(builder.getColumn(), builder.getRow(), 1, 2, "fill, fill"));

        StyleConstants.setForeground(this.errorAttrs, Color.red);
        // because we override getScrollableTracksViewportWidth:
        scrollPane.getViewport().setBackground(Color.white);
        StyleConstants.setBackground(this.errorAttrs, Color.white); // TODO mime 20080124: testing

        // the default table cell renderer uses a JLabel to render the heading and we want to
        // left align the header columns
        // TODO mime 20080415: left align with small spaces would be better
        final JTableHeader header = list.getTableHeader();
        ((JLabel) header.getDefaultRenderer()).setHorizontalAlignment(JLabel.LEFT);
        //set height of header correctly
        header.setPreferredSize(new Dimension(list.getTableHeader().getWidth(),
                (int) (1.1 * this.getFontMetrics(getFont()).getHeight())));

        changeHeaderWidth();
        final Thread refresh = new Thread() {
            public void run() {
                while (automaticRefresh) {
                    updateView();
                    IoUtility.sleep(5000); // refresh every 5 seconds
                }
            }
        };
        refresh.setDaemon(true);
        refresh.start();
    }

    /**
     * Make 2. column smaller.
     */
    private void changeHeaderWidth() {
        TableColumnModel columnModel = list.getColumnModel();
        columnModel.getColumn(0).setPreferredWidth(24);
        columnModel.getColumn(0).setMinWidth(24);
        columnModel.getColumn(1).setPreferredWidth(200);
        columnModel.getColumn(1).setMinWidth(150);
        columnModel.getColumn(2).setPreferredWidth(200);
        columnModel.getColumn(2).setMinWidth(150);
        columnModel.getColumn(3).setPreferredWidth(100);
        columnModel.getColumn(3).setMinWidth(100);
        columnModel.getColumn(4).setPreferredWidth(100);
        columnModel.getColumn(4).setMinWidth(100);
        columnModel.getColumn(5).setPreferredWidth(100);
        columnModel.getColumn(5).setMinWidth(100);
        columnModel.getColumn(6).setPreferredWidth(60);
        columnModel.getColumn(6).setMinWidth(60);
        columnModel.getColumn(7).setPreferredWidth(2000);
        columnModel.getColumn(7).setMinWidth(100);
        columnModel.getColumn(8).setPreferredWidth(200);
        columnModel.getColumn(8).setMinWidth(100);
    }

    /**
     * Update from model.
     */
    public synchronized void updateView() {
        final String method = "updateView";
        Trace.begin(CLASS, this, method);
        ((AbstractTableModel) list.getModel()).fireTableDataChanged();
        list.invalidate();
        this.revalidate();
        // TODO 20130228 remove me if refresh works
        //        list.repaint();
        //        this.repaint();
    }

    public void refreshStates() {
        Runnable stateChanged = new Runnable() {
            public void run() {
                updateView();
            }
        };
        SwingUtilities.invokeLater(stateChanged);
    }

    /**
     * We render our cells with this class.
     *
     * @author  Michael Meyling
     */
    private static class IconCellRenderer extends DefaultTableCellRenderer {

        protected void setValue(final Object value) {
            if (value instanceof Icon) {
                setIcon((Icon) value);
                super.setValue(null);
            } else {
                setIcon(null);
                super.setValue(value);
            }
        }

    }

    public void setRunningOnly(final boolean runningOnly) {
        model.setOnlyRunning(runningOnly);
        selectedProcesses.clear();
        list.getSelectionModel().clearSelection();
        refreshStates();
    }

    public void stopSelected() {
        Iterator iterator = selectedProcesses.iterator();
        while (iterator.hasNext()) {
            final ServiceProcess process = (ServiceProcess) iterator.next();
            if (process != null && process.isRunning()) {
                process.interrupt();
            }
        }
    }

    /**
     * Print stack trace of selected service processes to new window if the
     * method <code>Thread.getStackTrace()</code> is supported form the currently running java
     * version.
     */
    public void stackTraceSelected() {
        if (!selectedProcesses.isEmpty()) {
            StringBuffer result = new StringBuffer();
            Iterator iterator = selectedProcesses.iterator();
            while (iterator.hasNext()) {
                final ServiceProcess process = (ServiceProcess) iterator.next();
                if (process != null && process.isRunning()
                        && YodaUtility.existsMethod(Thread.class, "getStackTrace", new Class[] {})) {
                    StackTraceElement[] trace = new StackTraceElement[] {};
                    result.append("id ").append(process.getId());
                    try {
                        trace = (StackTraceElement[]) YodaUtility.executeMethod(process.getThread(),
                                "getStackTrace", new Class[] {}, new Object[] {});
                    } catch (NoSuchMethodException e) {
                        // ignore
                    } catch (InvocationTargetException e) {
                        // ignore
                    }
                    for (int i = 0; i < trace.length; i++) {
                        result.append(trace[i]);
                        result.append("\n");
                    }
                    result.append("\n\n");
                }
            }
            (new TextPaneWindow("Stacktrace", GuiHelper.readImageIcon("tango/16x16/devices/video-display.png"),
                    result.toString())).setVisible(true);
        }
    }

    /**
     * Print details of selected service processes new window.
     */
    public void detailsSelected() {
        if (!selectedProcesses.isEmpty()) {
            StringBuffer result = new StringBuffer();
            Iterator iterator = selectedProcesses.iterator();
            while (iterator.hasNext()) {
                final ServiceProcess process = (ServiceProcess) iterator.next();
                if (process != null) {
                    result.append("id ").append(process.getId());
                    String tip = "";
                    if (process.isBlocked()) {
                        tip = "Process is waiting";
                    } else if (process.isRunning()) {
                        tip = "Process is running";
                    } else if (process.wasFailure()) {
                        tip = "Process was stopped.";
                    } else if (process.wasSuccess()) {
                        tip = "Process has finished";
                    }
                    result.append("\n\tStatus:     ").append(tip);
                    result.append("\n\tAction:     ").append(process.getActionName());
                    result.append("\n\tModule:     ").append(process.getQedeqName());
                    result.append("\n\tURL:        ").append(process.getQedeqUrl());
                    result.append("\n\tStart:      ").append(DateUtility.getIsoTime(process.getStart()));
                    result.append("\n\tStop:       ")
                            .append((process.getStop() != 0 ? DateUtility.getIsoTime(process.getStop()) : ""));
                    result.append("\n\tPercentage: ").append(process.getExecutionPercentage());
                    result.append("\n\tDescription:").append(process.getExecutionActionDescription());
                    result.append("\n\tBlocked:   ");
                    final QedeqBoSet blocked = process.getBlockedModules();
                    Iterator bIterator = blocked.iterator();
                    while (bIterator.hasNext()) {
                        final QedeqBo qedeq = (QedeqBo) bIterator.next();
                        result.append(" ").append(qedeq.getName());
                    }
                    result.append("\n\tCalls:   ");
                    ServiceCall parent = process.getServiceCall();
                    while (parent != null) {
                        result.append("\n\t\t ").append(parent.getId());
                        tip = "";
                        if (!parent.isRunning()) {
                            tip = "Call is finished";
                        } else if (parent.isPaused()) {
                            tip = "Call is paused";
                        } else {
                            tip = "Call is running";
                        }
                        result.append("\n\t\t Status:     ").append(tip);
                        result.append("\n\t\t Module:     ").append(parent.getQedeq().getName());
                        result.append("\n\t\t Plugin:     ").append(parent.getService().getServiceAction());
                        result.append("\n\t\t Begin:      ").append(DateUtility.getIsoTime(parent.getBeginTime()));
                        result.append("\n\t\t End:        ")
                                .append((!parent.isRunning() ? DateUtility.getIsoTime(parent.getEndTime()) : ""));
                        result.append("\n\t\t Percentage: ").append(parent.getExecutionPercentage());
                        result.append("\n\t\t Description:").append(parent.getAction());
                        result.append("\n\t\t Config Par.:").append(parent.getConfigParametersString());
                        result.append("\n\t\t Parameter:  ").append(parent.getParametersString());

                        parent = parent.getParentServiceCall();
                    }
                    result.append("\n");
                }
            }
            (new TextPaneWindow("Service Process Details",
                    GuiHelper.readImageIcon("tango/16x16/devices/video-display.png"), result.toString()))
                            .setVisible(true);
        }
    }

}