weka.gui.beans.KnowledgeFlowRemoteSchedulerPerspective.java Source code

Java tutorial

Introduction

Here is the source code for weka.gui.beans.KnowledgeFlowRemoteSchedulerPerspective.java

Source

/*
 *   This program 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 3 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.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 *    KnowledgeFlowRemoteSchedulerPerspective.java
 *    Copyright (C) 2011-2013 University of Waikato, Hamilton, New Zealand
 */

package weka.gui.beans;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.beancontext.BeanContextSupport;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.StringReader;
import java.util.Calendar;
import java.util.Date;
import java.util.EventObject;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.zip.GZIPInputStream;

import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListModel;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.plaf.basic.BasicButtonUI;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.mortbay.jetty.security.Password;

import weka.core.Environment;
import weka.core.Instances;
import weka.core.WekaPackageManager;
import weka.experiment.TaskStatusInfo;
import weka.gui.ListSelectorDialog;
import weka.gui.beans.KnowledgeFlowApp.KFPerspective;
import weka.gui.beans.KnowledgeFlowApp.MainKFPerspective;
import weka.gui.beans.xml.XMLBeans;
import weka.server.ExecuteTaskServlet;
import weka.server.GetScheduleServlet;
import weka.server.GetTaskListServlet;
import weka.server.GetTaskResultServlet;
import weka.server.GetTaskStatusServlet;
import weka.server.NamedTask;
import weka.server.RootServlet;
import weka.server.Schedule;
import weka.server.WekaServer;
import weka.server.WekaServlet;
import weka.server.WekaTaskMap;
import weka.server.knowledgeFlow.FlowRunnerRemote;
import weka.server.knowledgeFlow.ScheduledNamedKFTask;
import weka.server.knowledgeFlow.UnscheduledNamedKFTask;

import com.toedter.calendar.JDateChooser;

/**
 * Class that provides a Knowledge Flow perspective for executing, scheduling
 * and monitoring tasks on remote Weka Server instances
 * 
 * @author Mark Hall (mhall{[at]}pentaho{[dot]}com)
 * @version $Revision$
 */
public class KnowledgeFlowRemoteSchedulerPerspective extends JPanel implements KFPerspective {

    /**
     * For serialization
     */
    private static final long serialVersionUID = -8233265107208192698L;

    /**
     * Inner class providing a panel for configuring and testing a connection to a
     * server
     */
    protected class HostPanel extends JPanel {

        /**
         * For serialization
         */
        private static final long serialVersionUID = 7893017818866180239L;

        /** Text field for entering the host name */
        protected JTextField m_hostField = new JTextField(25);

        /** Text field for entering the port number */
        protected JTextField m_portField = new JTextField(8);

        /** Button for testing the configured connection details */
        protected JButton m_testBut = new JButton("Test connection");

        /** Label for displaying server status (and load level) */
        protected JLabel m_serverStatus = new JLabel();

        /**
         * Constructor
         */
        public HostPanel() {
            setLayout(new BorderLayout());
            setBorder(BorderFactory.createTitledBorder("Remote host"));

            JPanel temp = new JPanel();
            temp.setLayout(new BorderLayout());

            JPanel holder1 = new JPanel();
            holder1.setLayout(new BorderLayout());
            holder1.add(new JLabel("Host: ", SwingConstants.RIGHT), BorderLayout.WEST);
            holder1.add(m_hostField, BorderLayout.CENTER);

            JPanel holder2 = new JPanel();
            holder2.setLayout(new BorderLayout());
            holder2.add(new JLabel("Port: ", SwingConstants.RIGHT), BorderLayout.WEST);
            holder2.add(m_portField, BorderLayout.CENTER);

            JPanel holder3 = new JPanel();
            holder3.setLayout(new BorderLayout());
            holder3.add(holder1, BorderLayout.CENTER);
            holder3.add(holder2, BorderLayout.EAST);

            temp.add(holder3, BorderLayout.NORTH);

            m_hostField.setText("localhost");
            m_portField.setText("" + WekaServer.PORT);

            JPanel holder4 = new JPanel();
            holder4.setLayout(new BorderLayout());
            holder4.add(m_testBut, BorderLayout.WEST);

            JPanel holder5 = new JPanel();
            holder5.setLayout(new BorderLayout());
            holder5.setBorder(BorderFactory.createTitledBorder("Server status"));
            holder5.add(m_serverStatus, BorderLayout.CENTER);
            holder5.add(holder4, BorderLayout.WEST);

            temp.add(holder5, BorderLayout.SOUTH);

            add(temp, BorderLayout.NORTH);

            m_testBut.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    hostOK();
                }
            });
        }

        /**
         * Get the host name
         * 
         * @return the host name
         */
        public String getHostName() {
            return m_hostField.getText();
        }

        /**
         * Get the port
         * 
         * @return the port
         */
        public String getPort() {
            return m_portField.getText();
        }

        /*
         * (non-Javadoc)
         * 
         * @see java.awt.Component#toString()
         */
        @Override
        public String toString() {
            String hostPort = m_hostField.getText() + ":" + m_portField.getText();
            return hostPort;
        }

        /**
         * Test the connection details
         * 
         * @return true if a connection can be made to the server
         */
        public boolean hostOK() {
            if (m_hostField.getText() != null && m_hostField.getText().length() > 0) {
                String host = m_hostField.getText();
                String port = "" + WekaServer.PORT;
                if (m_portField.getText() != null && m_portField.getText().length() > 0) {
                    port = m_portField.getText();
                }

                try {
                    host = Environment.getSystemWide().substitute(host);
                } catch (Exception ex) {
                }
                try {
                    port = Environment.getSystemWide().substitute(port);
                } catch (Exception ex) {
                }

                String server = host + ":" + port;
                double serverLoad = RootServlet.getSlaveLoad(server, m_username, m_password);
                if (serverLoad < 0) {
                    m_serverStatus.setText("Unable to connect to server.");
                    return false;
                } else {
                    m_serverStatus.setText("OK. Server load: " + serverLoad);
                    return true;
                }
            }
            return false;
        }
    }

    /**
     * Inner class providing a panel for configuring task scheduling details
     */
    protected class SchedulePanel extends JPanel {

        /**
         * For serialization
         */
        private static final long serialVersionUID = -1319029770018027370L;

        /**
         * Inner class to the SchedulePanel providing a date time configuration
         * panel
         */
        protected class DateTimePanel extends JPanel {

            /**
             * For serialization
             */
            private static final long serialVersionUID = 5343421783518237483L;

            /** A widget for selecting a date */
            protected JDateChooser m_dateChooser;
            /** Combo for choosing the hour of the day */
            protected JComboBox m_hourCombo = new JComboBox();
            /** Combo for choosing the minute of the hour */
            protected JComboBox m_minutesCombo = new JComboBox();
            /** Radio button for selecting execution now (or no end date) */
            protected JRadioButton m_nowNoEndBut;
            /** Radio button for selecting a specific date */
            protected JRadioButton m_dateBut = new JRadioButton("Date", false);

            /**
             * Construct a new DateTimePanel
             * 
             * @param title title to display in the border of the panel
             * @param now true if the now/no-end radio button should be labeled "now"
             */
            public DateTimePanel(String title, boolean now) {
                super();
                setLayout(new BorderLayout());
                setBorder(BorderFactory.createTitledBorder(title));

                m_dateChooser = new JDateChooser(new Date(), "yyyy-MM-dd");
                if (now) {
                    m_nowNoEndBut = new JRadioButton("Now (unscheduled)", true);
                } else {
                    m_nowNoEndBut = new JRadioButton("No end", true);
                }
                ButtonGroup b = new ButtonGroup();
                b.add(m_nowNoEndBut);
                b.add(m_dateBut);

                Vector<String> hours = new Vector<String>();
                for (int i = 0; i < 24; i++) {
                    String num = (i < 10) ? "0" + i : "" + i;
                    hours.add(num);
                }
                m_hourCombo.setModel(new DefaultComboBoxModel(hours));

                Vector<String> minutes = new Vector<String>();
                for (int i = 0; i < 60; i++) {
                    String num = (i < 10) ? "0" + i : "" + i;
                    minutes.add(num);
                }
                m_minutesCombo.setModel(new DefaultComboBoxModel(minutes));

                JPanel topHolder = new JPanel();
                topHolder.setLayout(new BorderLayout());
                JPanel temp1 = new JPanel();
                temp1.setLayout(new FlowLayout(FlowLayout.LEFT));
                temp1.add(m_nowNoEndBut);
                JPanel temp2 = new JPanel();
                temp2.setLayout(new FlowLayout(FlowLayout.LEFT));
                temp2.add(m_dateBut);
                temp2.add(m_dateChooser);
                topHolder.add(temp1, BorderLayout.NORTH);
                topHolder.add(temp2, BorderLayout.SOUTH);

                JPanel temp3 = new JPanel();
                temp3.setLayout(new FlowLayout(FlowLayout.LEFT));
                JPanel padder = new JPanel();
                padder.setMinimumSize(m_dateBut.getMinimumSize());
                temp3.add(padder);
                temp3.add(new JLabel("Time"));
                temp3.add(m_hourCombo);
                temp3.add(m_minutesCombo);

                JPanel holder = new JPanel();
                holder.setLayout(new BorderLayout());
                holder.add(topHolder, BorderLayout.NORTH);
                holder.add(temp3, BorderLayout.SOUTH);

                add(holder, BorderLayout.NORTH);

                m_nowNoEndBut.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        boolean enable = !m_nowNoEndBut.isSelected();

                        m_dateChooser.setEnabled(enable);
                        m_hourCombo.setEnabled(enable);
                        m_minutesCombo.setEnabled(enable);
                    }
                });

                m_dateBut.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        boolean enable = !m_nowNoEndBut.isSelected();

                        m_dateChooser.setEnabled(enable);
                        m_hourCombo.setEnabled(enable);
                        m_minutesCombo.setEnabled(enable);
                    }
                });

                m_dateChooser.setEnabled(false);
                m_hourCombo.setEnabled(false);
                m_minutesCombo.setEnabled(false);
            }

            /**
             * Get a Date object for the date configured in this panel
             * 
             * @return the configured date
             */
            public Date getDate() {
                if (m_nowNoEndBut.isSelected()) {
                    return null;
                }

                Date d = new Date(m_dateChooser.getDate().getTime());
                GregorianCalendar cal = new GregorianCalendar();
                cal.setTime(d);
                cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(m_hourCombo.getSelectedItem().toString()));
                cal.set(Calendar.MINUTE, Integer.parseInt(m_minutesCombo.getSelectedItem().toString()));

                // resolution to the nearest minute
                cal.set(Calendar.SECOND, 0);
                cal.set(Calendar.MILLISECOND, 0);

                return cal.getTime();
            }

            /**
             * Add a listener to radio button state changes
             * 
             * @param listener the listener to add
             */
            public void addRadioActionListener(ActionListener listener) {
                m_nowNoEndBut.addActionListener(listener);
                m_dateBut.addActionListener(listener);
            }
        }

        /**
         * Inner class to the SchedulePanel providing a panel for configuring repeat
         * details
         */
        protected class RepeatPanel extends JPanel {

            /**
             * For serialization
             */
            private static final long serialVersionUID = 749883190997695194L;

            /** The enabled status of the entire panel */
            protected boolean m_globalEnabled = true;

            /** Combo box for the repeat type */
            protected JComboBox m_repeatCombo = new JComboBox();

            /** Panel to hold the specific configuration widgets for each repeat type */
            protected JPanel m_detailPanel = new JPanel();

            /** Label for the interval */
            protected JLabel m_intervalLab = new JLabel("", SwingConstants.LEFT);

            /** Text field for the interval */
            protected JTextField m_intervalField = new JTextField(5);

            /** Labels for monthly details */
            protected JLabel m_ofEveryMonthLab = new JLabel("of every month");
            protected JLabel m_ofEveryMonthLab2 = new JLabel("of every month");

            /** Weekly stuff */
            protected JCheckBox m_sunCheck = new JCheckBox("Sunday");
            protected JCheckBox m_monCheck = new JCheckBox("Monday");
            protected JCheckBox m_tueCheck = new JCheckBox("Tuesday");
            protected JCheckBox m_wedCheck = new JCheckBox("Wednesday");
            protected JCheckBox m_thuCheck = new JCheckBox("Thursday");
            protected JCheckBox m_friCheck = new JCheckBox("Friday");
            protected JCheckBox m_satCheck = new JCheckBox("Saturday");

            /** Monthly stuff */
            protected JRadioButton m_dayYearNumBut = new JRadioButton("");
            protected JRadioButton m_specificDayYearBut = new JRadioButton("The");
            protected JTextField m_dayYearNumField = new JTextField(5);
            protected JComboBox m_occurrenceInMonth = new JComboBox();
            protected JComboBox m_dayOfWeek = new JComboBox();
            protected JComboBox m_month = new JComboBox();

            /**
             * Construct a new RepeatPanel
             */
            public RepeatPanel() {
                setLayout(new BorderLayout());
                setBorder(BorderFactory.createTitledBorder("Repeat"));

                Vector<String> repeats = new Vector<String>();
                for (Schedule.Repeat r : Schedule.Repeat.values()) {
                    repeats.add(r.toString());
                }
                m_repeatCombo.setModel(new DefaultComboBoxModel(repeats));

                JPanel holder = new JPanel();
                holder.setLayout(new BorderLayout());
                holder.add(m_repeatCombo, BorderLayout.NORTH);

                m_repeatCombo.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        String selected = m_repeatCombo.getSelectedItem().toString();
                        if (selected.equals("once")) {
                            setupOnce();
                            return;
                        }
                        if (selected.equals("minutes")) {
                            setupInterval("minute(s)");
                            return;
                        }
                        if (selected.equals("hours")) {
                            setupInterval("hour(s)");
                            return;
                        }
                        if (selected.equals("days")) {
                            setupInterval("day(s)");
                            return;
                        }
                        if (selected.equals("weekly")) {
                            setupWeekly();
                            return;
                        }
                        if (selected.equals("monthly")) {
                            setupMonthly();
                            return;
                        }
                        if (selected.equals("yearly")) {
                            setupYearly();
                        }
                    }
                });

                ButtonGroup group = new ButtonGroup();
                group.add(m_dayYearNumBut);
                group.add(m_specificDayYearBut);
                m_dayYearNumBut.setSelected(true);
                m_dayYearNumField.setEnabled(m_dayYearNumBut.isSelected());
                m_occurrenceInMonth.setEnabled(m_specificDayYearBut.isSelected());
                m_dayOfWeek.setEnabled(m_specificDayYearBut.isSelected());
                m_month.setEnabled(m_specificDayYearBut.isSelected());
                m_dayYearNumBut.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        checkEnabled();
                    }
                });
                m_specificDayYearBut.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        checkEnabled();
                    }
                });

                Vector<String> occur = new Vector<String>();
                for (Schedule.OccurrenceWithinMonth o : Schedule.OccurrenceWithinMonth.values()) {
                    occur.add(o.toString());
                }
                m_occurrenceInMonth.setModel(new DefaultComboBoxModel(occur));
                Vector<String> days = new Vector<String>();
                days.add("Sunday");
                days.add("Monday");
                days.add("Tuesday");
                days.add("Wednesday");
                days.add("Thursday");
                days.add("Friday");
                days.add("Saturday");
                m_dayOfWeek.setModel(new DefaultComboBoxModel(days));

                Vector<String> months = new Vector<String>();
                months.add("January");
                months.add("February");
                months.add("March");
                months.add("April");
                months.add("May");
                months.add("June");
                months.add("July");
                months.add("August");
                months.add("September");
                months.add("October");
                months.add("November");
                months.add("December");
                m_month.setModel(new DefaultComboBoxModel(months));

                m_detailPanel.setLayout(new BorderLayout());
                holder.add(m_detailPanel, BorderLayout.SOUTH);
                add(holder, BorderLayout.NORTH);
            }

            private void checkEnabled() {

                m_dayYearNumField.setEnabled(m_dayYearNumBut.isSelected() && m_globalEnabled);
                m_occurrenceInMonth.setEnabled(m_specificDayYearBut.isSelected() && m_globalEnabled);
                m_dayOfWeek.setEnabled(m_specificDayYearBut.isSelected() && m_globalEnabled);
                m_month.setEnabled(m_specificDayYearBut.isSelected() && m_globalEnabled);
                m_ofEveryMonthLab.setEnabled(m_dayYearNumBut.isSelected() && m_globalEnabled);
                m_ofEveryMonthLab2.setEnabled(m_specificDayYearBut.isSelected() && m_globalEnabled);

                m_repeatCombo.setEnabled(m_globalEnabled);
                m_intervalLab.setEnabled(m_globalEnabled);
                m_intervalField.setEnabled(m_globalEnabled);
                m_sunCheck.setEnabled(m_globalEnabled);
                m_monCheck.setEnabled(m_globalEnabled);
                m_tueCheck.setEnabled(m_globalEnabled);
                m_wedCheck.setEnabled(m_globalEnabled);
                m_thuCheck.setEnabled(m_globalEnabled);
                m_friCheck.setEnabled(m_globalEnabled);
                m_satCheck.setEnabled(m_globalEnabled);
                m_dayYearNumBut.setEnabled(m_globalEnabled);
                m_specificDayYearBut.setEnabled(m_globalEnabled);

                // m_dayYearNumField.setEnabled(m_globalEnabled);
                // m_occurrenceInMonth.setEnabled(m_globalEnabled);
                // m_dayOfWeek.setEnabled(m_globalEnabled);
                // m_month.setEnabled(m_globalEnabled);
            }

            private void setupOnce() {
                m_detailPanel.removeAll();
                revalidate();
                repaint();
            }

            private void setupInterval(String label) {
                m_detailPanel.removeAll();
                JPanel temp = new JPanel();
                temp.setLayout(new FlowLayout(FlowLayout.LEFT));
                temp.add(new JLabel("Every", SwingConstants.RIGHT));
                m_intervalLab.setText(label);
                temp.add(m_intervalField);
                temp.add(m_intervalLab);
                m_detailPanel.add(temp, BorderLayout.NORTH);
                revalidate();
                repaint();
            }

            private void setupWeekly() {
                m_detailPanel.removeAll();
                JLabel lab = new JLabel("Recur every week on:");
                JPanel temp = new JPanel();
                temp.setLayout(new FlowLayout(FlowLayout.LEFT));
                temp.add(lab);
                JPanel temp2 = new JPanel();
                temp2.setLayout(new GridLayout(2, 4));
                temp2.add(m_sunCheck);
                temp2.add(m_monCheck);
                temp2.add(m_tueCheck);
                temp2.add(m_wedCheck);
                temp2.add(m_thuCheck);
                temp2.add(m_friCheck);
                temp2.add(m_satCheck);
                m_detailPanel.add(temp, BorderLayout.NORTH);
                m_detailPanel.add(temp2, BorderLayout.CENTER);
                revalidate();
                repaint();
            }

            private void setupMonthly() {
                m_detailPanel.removeAll();

                JPanel topP = new JPanel();
                m_dayYearNumBut.setText("Day");
                topP.setLayout(new FlowLayout(FlowLayout.LEFT));
                topP.add(m_dayYearNumBut);
                topP.add(m_dayYearNumField);
                topP.add(m_ofEveryMonthLab);

                JPanel botP = new JPanel();
                botP.setLayout(new FlowLayout(FlowLayout.LEFT));
                botP.add(m_specificDayYearBut);
                botP.add(m_occurrenceInMonth);
                botP.add(m_dayOfWeek);
                botP.add(m_ofEveryMonthLab2);
                JPanel holder = new JPanel();
                holder.setLayout(new BorderLayout());
                holder.add(topP, BorderLayout.NORTH);
                holder.add(botP, BorderLayout.SOUTH);
                m_detailPanel.add(holder, BorderLayout.NORTH);
                revalidate();
                repaint();
            }

            private void setupYearly() {
                m_detailPanel.removeAll();

                m_dayYearNumBut.setText("Every");

                JPanel topP = new JPanel();
                topP.setLayout(new FlowLayout(FlowLayout.LEFT));
                topP.add(m_dayYearNumBut);
                topP.add(m_dayYearNumField);
                topP.add(new JLabel("years"));

                JPanel botP = new JPanel();
                botP.setLayout(new FlowLayout(FlowLayout.LEFT));
                botP.add(m_specificDayYearBut);
                botP.add(m_occurrenceInMonth);
                botP.add(m_dayOfWeek);
                botP.add(new JLabel("of"));
                botP.add(m_month);
                JPanel holder = new JPanel();
                holder.setLayout(new BorderLayout());
                holder.add(topP, BorderLayout.NORTH);
                holder.add(botP, BorderLayout.SOUTH);
                m_detailPanel.add(holder, BorderLayout.NORTH);
                revalidate();
                repaint();
            }

            /**
             * Set the global enabled status
             * 
             * @param enabled true if panel is enabled globally
             */
            @Override
            public void setEnabled(boolean enabled) {
                super.setEnabled(enabled);

                m_globalEnabled = enabled;
                checkEnabled();
            }

            /**
             * Apply the panel's settings to a Schedule object
             * 
             * @param sched the schedule to configure
             */
            public void applyToSchedule(Schedule sched) {
                String repeat = m_repeatCombo.getSelectedItem().toString();
                for (Schedule.Repeat r : Schedule.Repeat.values()) {
                    if (r.toString().equals(repeat)) {
                        sched.setRepeatUnit(r);
                        break;
                    }
                }
                if (m_intervalField.getText() != null && m_intervalField.getText().length() > 0) {
                    sched.setRepeatValue(Integer.parseInt(m_intervalField.getText()));
                } else if (sched.getRepeatUnit() == Schedule.Repeat.MINUTES
                        || sched.getRepeatUnit() == Schedule.Repeat.HOURS
                        || sched.getRepeatUnit() == Schedule.Repeat.DAYS) {
                    // set a default value of 5
                    sched.setRepeatValue(5);
                    m_intervalField.setText("5");
                }

                java.util.List<Integer> dow = new java.util.ArrayList<Integer>();
                if (m_sunCheck.isSelected()) {
                    dow.add(Calendar.SUNDAY);
                }
                if (m_monCheck.isSelected()) {
                    dow.add(Calendar.MONDAY);
                }
                if (m_tueCheck.isSelected()) {
                    dow.add(Calendar.TUESDAY);
                }
                if (m_wedCheck.isSelected()) {
                    dow.add(Calendar.WEDNESDAY);
                }
                if (m_thuCheck.isSelected()) {
                    dow.add(Calendar.THURSDAY);
                }
                if (m_friCheck.isSelected()) {
                    dow.add(Calendar.FRIDAY);
                }
                if (m_satCheck.isSelected()) {
                    dow.add(Calendar.SATURDAY);
                }
                sched.setDayOfTheWeek(dow);

                if (sched.getRepeatUnit() == Schedule.Repeat.MONTHLY) {
                    if (m_dayYearNumBut.isSelected()) {
                        if (m_dayYearNumField.getText() != null && m_dayYearNumField.getText().length() > 0) {
                            sched.setDayOfTheMonth(Integer.parseInt(m_dayYearNumField.getText()));
                        }
                    } else {
                        for (Schedule.OccurrenceWithinMonth o : Schedule.OccurrenceWithinMonth.values()) {
                            if (o.equals(m_occurrenceInMonth.getSelectedItem().toString())) {
                                sched.setOccurrenceWithinMonth(o);
                                break;
                            }
                        }
                    }
                }

                if (sched.getRepeatUnit() == Schedule.Repeat.YEARLY) {
                    if (m_dayYearNumBut.isSelected()) {
                        if (m_dayYearNumField.getText() != null && m_dayYearNumField.getText().length() > 0) {
                            sched.setRepeatValue(Integer.parseInt(m_dayYearNumField.getText()));
                        }
                    } else {
                        for (Schedule.OccurrenceWithinMonth o : Schedule.OccurrenceWithinMonth.values()) {
                            if (o.equals(m_occurrenceInMonth.getSelectedItem().toString())) {
                                sched.setOccurrenceWithinMonth(o);
                                break;
                            }
                        }
                        // day of the week is already set

                        sched.setMonthOfTheYear(m_month.getSelectedIndex());
                    }
                }
            }
        }

        /**
         * Inner class providing a panel for configuring flow variables
         */
        protected class ParameterPanel extends JPanel {

            /**
             * For serialization
             */
            private static final long serialVersionUID = -491659781619944544L;

            /** The JTable for configuring variables */
            protected InteractiveTablePanel m_table = new InteractiveTablePanel(
                    new String[] { "Variable", "Value", "" });

            /**
             * Construct a new ParameterPanel
             */
            public ParameterPanel() {
                setLayout(new BorderLayout());
                setBorder(BorderFactory.createTitledBorder("Flow variables"));
                add(m_table, BorderLayout.CENTER);
            }

            /**
             * Get the configured variables
             * 
             * @return a Map of variable names and values
             */
            public Map<String, String> getParameters() {

                Map<String, String> result = new HashMap<String, String>();
                JTable table = m_table.getTable();
                int numRows = table.getModel().getRowCount();

                for (int i = 0; i < numRows; i++) {
                    String paramName = table.getValueAt(i, 0).toString();
                    String paramValue = table.getValueAt(i, 1).toString();
                    if (paramName.length() > 0 && paramValue.length() > 0) {
                        result.put(paramName, paramValue);
                    }
                }

                return result;
            }
        }

        /**
         * Inner class providing a JList dialog of flows from the main KF
         * perspective. The user can select one or more flows from the list to
         * launch remotely
         */
        protected class FlowLauncher extends JPanel {

            /**
             * For serialization
             */
            private static final long serialVersionUID = 5635324388653056125L;
            protected JButton m_popupFlowSelector = new JButton("Launch flows remotely");
            protected JCheckBox m_sequentialCheck = new JCheckBox("Launch flow start points sequentially");
            protected DefaultListModel m_flowModel = new DefaultListModel();
            protected JList m_flowList = new JList(m_flowModel);

            public FlowLauncher() {
                setLayout(new BorderLayout());
                m_popupFlowSelector.setToolTipText("Launch flow(s) using the specified schedule");
                JPanel holder = new JPanel();
                holder.setLayout(new BorderLayout());

                holder.add(m_popupFlowSelector, BorderLayout.WEST);
                holder.add(m_sequentialCheck, BorderLayout.EAST);
                add(holder, BorderLayout.WEST);

                m_popupFlowSelector.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        popupList();
                    }
                });
            }

            protected void popupList() {
                if (m_mainPerspective == null || m_mainPerspective.getNumTabs() == 0) {
                    return;
                }
                if (!m_hostPanel.hostOK()) {
                    JOptionPane.showMessageDialog(KnowledgeFlowRemoteSchedulerPerspective.this,
                            "Can't connect to host, so can't launch flows.", "SchedulePerspective",
                            JOptionPane.ERROR_MESSAGE);
                    System.err.println("Can't connect to host, so can't launch flows...");
                    return;
                }
                m_flowModel.removeAllElements();
                // int unamedCount = 1;
                for (int i = 0; i < m_mainPerspective.getNumTabs(); i++) {
                    String tabTitle = m_mainPerspective.getTabTitle(i);

                    /*
                     * File flowFile = m_mainPerspective.getFlowFile(i); if
                     * (flowFile.getName().equals("-NONE-")) {
                     * m_flowModel.addElement("unnamedFlow" + unamedCount++); } else {
                     * String flowName = flowFile.getName(); if (flowName.indexOf('.') >
                     * 0) { flowName = flowName.substring(0, flowName.lastIndexOf('.')); }
                     * m_flowModel.addElement(flowName); }
                     */
                    m_flowModel.addElement(tabTitle);
                }

                ListSelectorDialog jd = new ListSelectorDialog(null, m_flowList);

                // Open the dialog
                int result = jd.showDialog();

                if (result != ListSelectorDialog.APPROVE_OPTION) {
                    m_flowList.clearSelection();
                } else {
                    if (!m_flowList.isSelectionEmpty()) {
                        int selected[] = m_flowList.getSelectedIndices();
                        for (int element : selected) {
                            int flowIndex = element;

                            // only send to server if its not executing locally!
                            if (!m_mainPerspective.getExecuting(flowIndex)) {
                                String flowName = m_flowModel.get(flowIndex).toString();
                                if (launchFlow(flowIndex, flowName)) {
                                    // abort sending any other flows...
                                    break;
                                }
                            }
                        }
                    }
                }
            }

            /**
             * Launch a knowledge flow on the remote server
             * 
             * @param flowIndex the index, in the main perspective, of the flow to
             *          launch
             * @param flowName the name of the flow
             * @return true if the flow was launched without error.
             */
            protected boolean launchFlow(int flowIndex, String flowName) {
                Schedule sched = m_schedulePanel.getSchedule();

                StringBuffer flowXML = null;
                try {
                    flowXML = copyFlow(flowIndex);
                } catch (Exception ex) {
                    JOptionPane.showMessageDialog(KnowledgeFlowRemoteSchedulerPerspective.this,
                            "A problem occurred while copying the flow to execute:\n\n" + ex.getMessage(),
                            "SchedulePerspective", JOptionPane.ERROR_MESSAGE);
                    ex.printStackTrace();
                    return false;
                }

                NamedTask taskToRun = null;

                Map<String, String> params = m_parameters.getParameters();
                if (params.size() == 0) {
                    params = null;
                }
                if (sched == null) {
                    taskToRun = new UnscheduledNamedKFTask(FlowRunnerRemote.NAME_PREFIX + flowName, flowXML,
                            m_sequentialCheck.isSelected(), params);
                } else {
                    taskToRun = new ScheduledNamedKFTask(FlowRunnerRemote.NAME_PREFIX + flowName, flowXML,
                            m_sequentialCheck.isSelected(), params, sched);
                }

                InputStream is = null;
                PostMethod post = null;
                boolean errorOccurred = false;

                try {
                    byte[] serializedTask = WekaServer.serializeTask(taskToRun);
                    System.out.println("Sending " + serializedTask.length + " bytes...");

                    String service = ExecuteTaskServlet.CONTEXT_PATH + "/?client=Y";
                    post = new PostMethod(constructURL(service));
                    RequestEntity entity = new ByteArrayRequestEntity(serializedTask);
                    post.setRequestEntity(entity);

                    post.setDoAuthentication(true);
                    post.addRequestHeader(new Header("Content-Type", "application/octet-stream"));

                    // Get HTTP client
                    HttpClient client = WekaServer.ConnectionManager.getSingleton().createHttpClient();
                    WekaServer.ConnectionManager.addCredentials(client, m_username, m_password);

                    // Execute request
                    int result = client.executeMethod(post);
                    if (result == 401) {
                        JOptionPane.showMessageDialog(KnowledgeFlowRemoteSchedulerPerspective.this,
                                "Unable to send task to server - authentication required", "SchedulePerspective",
                                JOptionPane.ERROR_MESSAGE);
                        System.err.println("Unable to send task to server - authentication required.\n");
                        errorOccurred = true;
                    } else {
                        is = post.getResponseBodyAsStream();
                        ObjectInputStream ois = new ObjectInputStream(is);
                        Object response = ois.readObject();
                        if (response.toString().startsWith(WekaServlet.RESPONSE_ERROR)) {
                            System.err.println("A problem occurred at the sever : \n" + "\t" + response.toString());
                            JOptionPane.showMessageDialog(KnowledgeFlowRemoteSchedulerPerspective.this,
                                    "A problem occurred at the server : \n\n" + response.toString(),
                                    "SchedulePerspective", JOptionPane.ERROR_MESSAGE);
                            errorOccurred = true;
                        } else {
                            String taskID = response.toString();
                            System.out.println("Task ID from server : " + taskID);
                            m_monitorPanel.monitorTask(taskID);
                        }
                    }
                } catch (Exception ex) {
                    JOptionPane.showMessageDialog(KnowledgeFlowRemoteSchedulerPerspective.this,
                            "A problem occurred while sending task to the server : \n\n" + ex.getMessage(),
                            "SchedulePerspective", JOptionPane.ERROR_MESSAGE);
                    ex.printStackTrace();
                } finally {
                    if (is != null) {
                        try {
                            is.close();
                        } catch (Exception e) {
                        }
                    }

                    if (post != null) {
                        // Release current connection to the connection pool
                        post.releaseConnection();
                    }
                }
                return errorOccurred;
            }

            private StringBuffer copyFlow(int flowIndex) throws Exception {
                StringBuffer copy = new StringBuffer();
                Vector beans = BeanInstance.getBeanInstances(flowIndex);
                Vector associatedConnections = BeanConnection.getConnections(flowIndex);

                Vector v = new Vector();
                v.setSize(2);
                v.set(XMLBeans.INDEX_BEANINSTANCES, beans);
                v.set(XMLBeans.INDEX_BEANCONNECTIONS, associatedConnections);
                KnowledgeFlowApp.BeanLayout layout = m_mainPerspective.getBeanLayout(flowIndex);
                BeanContextSupport bcs = new BeanContextSupport();
                bcs.setDesignTime(true);

                XMLBeans xml = new XMLBeans(layout, bcs, flowIndex);
                java.io.StringWriter sw = new java.io.StringWriter();
                xml.write(sw, v);

                return sw.getBuffer();
            }
        }

        /** A panel for configuring the start date/time */
        protected DateTimePanel m_startDate;

        /** A panel for configuring the end date/time */
        protected DateTimePanel m_endDate;

        /** A panel for configuring the repeat */
        protected RepeatPanel m_repeat = new RepeatPanel();

        /** A panel for specifying flow variables and their values */
        protected ParameterPanel m_parameters = new ParameterPanel();

        /** Popup that lists the available flows from the main perspective */
        protected FlowLauncher m_launcher = new FlowLauncher();

        /**
         * Construct a new SchedulePanel
         */
        public SchedulePanel() {
            setLayout(new BorderLayout());
            setBorder(BorderFactory.createTitledBorder("Schedule"));
            JPanel holderPanel = new JPanel();
            holderPanel.setLayout(new BorderLayout());

            m_startDate = new DateTimePanel("Start", true);
            m_endDate = new DateTimePanel("End", false);
            JPanel temp1 = new JPanel();
            temp1.setLayout(new GridLayout(1, 2));
            temp1.add(m_startDate);
            temp1.add(m_endDate);
            holderPanel.add(temp1, BorderLayout.NORTH);

            JPanel temp2 = new JPanel();
            temp2.setLayout(new GridLayout(1, 2));
            temp2.add(m_repeat);
            temp2.add(m_parameters);
            holderPanel.add(temp2, BorderLayout.SOUTH);
            m_repeat.setEnabled(false);

            add(holderPanel, BorderLayout.NORTH);
            add(m_launcher, BorderLayout.SOUTH);

            // action listener to listen for changes in the start date panel
            // so that we can enable/disable the repeat panel
            m_startDate.addRadioActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (m_startDate.getDate() == null) {
                        m_repeat.setEnabled(false);
                    } else {
                        m_repeat.setEnabled(true);
                    }
                }
            });
        }

        /**
         * Get the schedule configured by this panel
         * 
         * @return the configured schedule
         */
        public Schedule getSchedule() {

            Schedule sched = new Schedule();

            if (m_startDate.getDate() != null) {
                sched.setStartDate(m_startDate.getDate());
            } else {
                return null; // execute unscheduled (right now)
            }
            if (m_endDate.getDate() != null) {
                sched.setEndDate(m_endDate.getDate());
            }

            m_repeat.applyToSchedule(sched);

            return sched;
        }
    }

    /**
     * Inner class providing a panel for monitoring flows running on the server.
     * 
     * @author Mark Hall (mhall{[at]}pentaho{[dot]}com)
     * @version $Revision$
     */
    protected class MonitorPanel extends JPanel {

        /**
         * For serialization
         */
        private static final long serialVersionUID = 6556408319133214173L;

        private class CloseableTabTitle extends JPanel {

            /**
             * For serialization
             */
            private static final long serialVersionUID = -871710265588121352L;
            private final JTabbedPane m_enclosingPane;
            private Thread m_monitorThread;

            private JLabel m_tabLabel;
            private TabButton m_tabButton;

            public CloseableTabTitle(final JTabbedPane pane) {
                super(new FlowLayout(FlowLayout.LEFT, 0, 0));

                m_enclosingPane = pane;
                setOpaque(false);
                setBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0));

                // read the title from the JTabbedPane
                m_tabLabel = new JLabel() {

                    @Override
                    public String getText() {
                        int index = m_enclosingPane.indexOfTabComponent(CloseableTabTitle.this);
                        if (index >= 0) {
                            return m_enclosingPane.getTitleAt(index);
                        }
                        return null;
                    }
                };

                add(m_tabLabel);
                m_tabLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 5));
                m_tabButton = new TabButton();
                add(m_tabButton);

            }

            /**
             * Set the thread that is monitoring this tab's flow
             * 
             * @param monitorThread the monitoring thread
             */
            public void setMonitorThread(Thread monitorThread) {
                m_monitorThread = monitorThread;
            }

            public void setBold(boolean bold) {
                m_tabLabel.setEnabled(bold);
            }

            public void setButtonEnabled(boolean enabled) {
                m_tabButton.setEnabled(enabled);
            }

            private class TabButton extends JButton implements ActionListener {

                /**
                 * For serialization
                 */
                private static final long serialVersionUID = 8050674024546615663L;

                public TabButton() {
                    int size = 17;
                    setPreferredSize(new Dimension(size, size));
                    setToolTipText("close this tab");
                    // Make the button looks the same for all Laf's
                    setUI(new BasicButtonUI());
                    // Make it transparent
                    setContentAreaFilled(false);
                    // No need to be focusable
                    setFocusable(false);
                    setBorder(BorderFactory.createEtchedBorder());
                    setBorderPainted(false);
                    // Making nice rollover effect
                    // we use the same listener for all buttons
                    addMouseListener(new MouseAdapter() {
                        @Override
                        public void mouseEntered(MouseEvent e) {
                            Component component = e.getComponent();

                            if (component instanceof AbstractButton) {
                                AbstractButton button = (AbstractButton) component;
                                button.setBorderPainted(true);
                            }
                        }

                        @Override
                        public void mouseExited(MouseEvent e) {
                            Component component = e.getComponent();
                            if (component instanceof AbstractButton) {
                                AbstractButton button = (AbstractButton) component;
                                button.setBorderPainted(false);
                            }
                        }
                    });
                    setRolloverEnabled(true);
                    // Close the proper tab by clicking the button
                    addActionListener(this);
                }

                @Override
                public void actionPerformed(ActionEvent e) {
                    int i = m_enclosingPane.indexOfTabComponent(CloseableTabTitle.this);
                    m_enclosingPane.remove(i);
                    if (m_monitorThread != null) {
                        m_monitorThread.interrupt();
                        m_monitorThread = null;
                    }
                }

                // we don't want to update UI for this button
                @Override
                public void updateUI() {
                }

                // paint the cross
                @Override
                protected void paintComponent(Graphics g) {
                    super.paintComponent(g);
                    Graphics2D g2 = (Graphics2D) g.create();
                    // shift the image for pressed buttons
                    if (getModel().isPressed()) {
                        g2.translate(1, 1);
                    }
                    g2.setStroke(new BasicStroke(2));
                    g2.setColor(Color.BLACK);
                    if (!isEnabled()) {
                        g2.setColor(Color.GRAY);
                    }
                    if (getModel().isRollover()) {
                        g2.setColor(Color.MAGENTA);
                    }
                    int delta = 6;
                    g2.drawLine(delta, delta, getWidth() - delta - 1, getHeight() - delta - 1);
                    g2.drawLine(getWidth() - delta - 1, delta, delta, getHeight() - delta - 1);
                    g2.dispose();
                }
            }
        }

        /** Provides tabs - one for each flow being monitored */
        protected JTabbedPane m_monitorTabs = new JTabbedPane();

        /** Combo box listing tasks on the server */
        protected JComboBox m_tasksOnServer = new JComboBox();

        /** Button to refresh the task combo */
        protected JButton m_refreshTaskBut = new JButton("Refresh task list");

        /** Button to start monitoring a task (opens a new tab) */
        protected JButton m_monitorBut = new JButton("Monitor");

        /** Field for entering an interal at which to poll the server */
        protected JTextField m_monitorIntervalField = new JTextField(5);

        /** Current monitoring interval */
        protected int m_monitorInterval = 15; // 15 seconds

        /**
         * Construct a new MontitorPanel
         */
        public MonitorPanel() {
            setLayout(new BorderLayout());
            setBorder(BorderFactory.createTitledBorder("Monitor tasks"));
            m_monitorIntervalField.setToolTipText("Task status monitoring interval (seconds)");
            m_monitorIntervalField.setText("" + m_monitorInterval);
            m_refreshTaskBut.setToolTipText("Fetch task list from server");
            m_monitorBut.setToolTipText("Start monitoring selected task");

            JPanel holder = new JPanel();
            holder.setLayout(new FlowLayout(FlowLayout.LEFT));
            holder.add(m_tasksOnServer);
            holder.add(m_refreshTaskBut);
            holder.add(m_monitorBut);
            holder.add(m_monitorIntervalField);
            m_monitorBut.setEnabled(false);
            m_monitorBut.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    String toMonitor = m_tasksOnServer.getSelectedItem().toString();
                    if (toMonitor != null && toMonitor.length() > 0) {
                        int interval = m_monitorInterval;
                        try {
                            interval = Integer.parseInt(m_monitorIntervalField.getText().trim());
                        } catch (NumberFormatException ex) {
                        }
                        m_monitorInterval = interval;
                        monitorTask(toMonitor);
                    }
                }
            });

            m_refreshTaskBut.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    boolean tasksOnServer = refreshTaskList();
                    m_monitorBut.setEnabled(tasksOnServer);
                }
            });

            add(holder, BorderLayout.NORTH);
            add(m_monitorTabs, BorderLayout.CENTER);

            Dimension d = m_refreshTaskBut.getPreferredSize();
            m_tasksOnServer.setMinimumSize(d);
            m_tasksOnServer.setPreferredSize(d);
        }

        /**
         * Retrieve a result from the server
         * 
         * @param taskID the ID of the task to get the result for
         * @return the TaskStatusInfo object encapsulating the result
         */
        protected TaskStatusInfo getResultFromServer(String taskID) {
            InputStream is = null;
            PostMethod post = null;
            TaskStatusInfo resultInfo = null;

            try {
                String service = GetTaskResultServlet.CONTEXT_PATH + "/?name=" + taskID + "&client=Y";
                post = new PostMethod(constructURL(service));
                post.setDoAuthentication(true);
                post.addRequestHeader(new Header("Content-Type", "text/plain"));

                // Get HTTP client
                HttpClient client = WekaServer.ConnectionManager.getSingleton().createHttpClient();
                WekaServer.ConnectionManager.addCredentials(client, m_username, m_password);

                int result = client.executeMethod(post);
                if (result == 401) {
                    System.err.println("Unable to retrieve task from server - authentication " + "required");
                } else {
                    is = post.getResponseBodyAsStream();
                    ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new GZIPInputStream(is)));
                    Object response = ois.readObject();
                    if (response.toString().startsWith(WekaServlet.RESPONSE_ERROR)) {

                        System.err.println(
                                "Server returned an error: " + "while trying to retrieve task result for task: '"
                                        + taskID + "'. (" + response.toString() + ")." + " Check logs on server.");
                    } else {
                        resultInfo = ((TaskStatusInfo) response);
                    }
                }
            } catch (Exception ex) {
                System.err.println("An error occurred while trying to retrieve task result from " + "server: "
                        + ex.getMessage());
                ex.printStackTrace();
            } finally {
                if (is != null) {
                    try {
                        is.close();
                    } catch (Exception e) {
                    }
                }

                if (post != null) {
                    // Release current connection to the connection pool
                    post.releaseConnection();
                }
            }

            return resultInfo;
        }

        /**
         * Retrieve scheduling information for the supplied task ID
         * 
         * @param taskName the ID of the task to get the schedule for from the
         *          server
         * 
         * @return the Schedule of the task
         */
        protected Schedule getScheduleForTask(String taskName) {

            InputStream is = null;
            PostMethod post = null;
            TaskStatusInfo resultInfo = null;
            Schedule sched = null;

            if (!m_hostPanel.hostOK()) {
                return null;
            }

            try {
                String service = GetScheduleServlet.CONTEXT_PATH + "/?name=" + taskName + "&client=Y";
                post = new PostMethod(constructURL(service));

                post.setDoAuthentication(true);
                post.addRequestHeader(new Header("Content-Type", "text/plain"));

                // Get HTTP client
                HttpClient client = WekaServer.ConnectionManager.getSingleton().createHttpClient();
                WekaServer.ConnectionManager.addCredentials(client, m_username, m_password);

                int result = client.executeMethod(post);
                if (result == 401) {
                    JOptionPane.showMessageDialog(KnowledgeFlowRemoteSchedulerPerspective.this,
                            "Unable to get schedule information from server - authentication required",
                            "SchedulePerspective", JOptionPane.ERROR_MESSAGE);
                    System.err.println("Unable to send task to get schedule info from server"
                            + " - authentication required.\n");
                } else {
                    is = post.getResponseBodyAsStream();
                    ObjectInputStream ois = new ObjectInputStream(is);
                    Object response = ois.readObject();

                    if (response.toString().indexOf("is not a scheduled task") > 0) {
                        // just return null
                    } else if (response.toString().startsWith(WekaServlet.RESPONSE_ERROR)) {
                        System.err.println("A problem occurred at the sever : \n" + "\t" + response.toString());
                        JOptionPane.showMessageDialog(KnowledgeFlowRemoteSchedulerPerspective.this,
                                "A problem occurred at the server : \n\n" + response.toString(),
                                "SchedulePerspective", JOptionPane.ERROR_MESSAGE);
                    } else {
                        sched = (Schedule) response;
                    }
                }
            } catch (Exception ex) {
                JOptionPane.showMessageDialog(KnowledgeFlowRemoteSchedulerPerspective.this,
                        "A problem occurred while trying to get schedule info from server : \n\n" + ex.getMessage(),
                        "SchedulePerspective", JOptionPane.ERROR_MESSAGE);
                ex.printStackTrace();
            } finally {
                if (is != null) {
                    try {
                        is.close();
                    } catch (Exception e) {
                    }
                }

                if (post != null) {
                    // Release current connection to the connection pool
                    post.releaseConnection();
                }
            }

            return sched;
        }

        /**
         * Refresh the task combo box
         * 
         * @return true if there are tasks available on the server
         */
        protected boolean refreshTaskList() {
            m_tasksOnServer.removeAllItems();

            if (!m_hostPanel.hostOK()) {
                return false;
            }

            InputStream is = null;
            PostMethod post = null;
            boolean tasksOnServer = false;

            try {
                String service = GetTaskListServlet.CONTEXT_PATH + "/?client=Y";
                post = new PostMethod(constructURL(service));

                post.setDoAuthentication(true);
                post.addRequestHeader(new Header("Content-Type", "text/plain"));

                // Get HTTP client
                HttpClient client = WekaServer.ConnectionManager.getSingleton().createHttpClient();
                WekaServer.ConnectionManager.addCredentials(client, m_username, m_password);

                // Execute request
                int result = client.executeMethod(post);
                if (result == 401) {
                    System.err.println(
                            "Unable to send task to get task list from server" + " - authentication required.\n");
                    JOptionPane.showMessageDialog(KnowledgeFlowRemoteSchedulerPerspective.this,
                            "Unable to get task list from server - authentication required", "SchedulePerspective",
                            JOptionPane.ERROR_MESSAGE);
                } else {
                    is = post.getResponseBodyAsStream();
                    ObjectInputStream ois = new ObjectInputStream(is);
                    Object response = ois.readObject();
                    if (response.toString().startsWith(WekaServlet.RESPONSE_ERROR)) {
                        System.err.println("A problem occurred at the sever : \n" + "\t" + response.toString());
                        JOptionPane.showMessageDialog(KnowledgeFlowRemoteSchedulerPerspective.this,
                                "A problem occurred at the server : \n\n" + response.toString(),
                                "SchedulePerspective", JOptionPane.ERROR_MESSAGE);
                    } else {
                        List<String> taskList = (List<String>) response;
                        if (taskList.size() > 0) {
                            Vector<String> newList = new Vector<String>();
                            for (String task : taskList) {
                                newList.add(task);
                            }
                            DefaultComboBoxModel model = new DefaultComboBoxModel(newList);
                            m_tasksOnServer.setModel(model);
                            tasksOnServer = true;
                        }
                    }
                }
            } catch (Exception ex) {
                JOptionPane.showMessageDialog(KnowledgeFlowRemoteSchedulerPerspective.this,
                        "A problem occurred at the server : \n\n" + ex.getMessage(), "SchedulePerspective",
                        JOptionPane.ERROR_MESSAGE);
                ex.printStackTrace();
            } finally {
                if (is != null) {
                    try {
                        is.close();
                    } catch (Exception e) {
                    }
                }

                if (post != null) {
                    // Release current connection to the connection pool
                    post.releaseConnection();
                }
            }

            return tasksOnServer;
        }

        /**
         * Add a new logging tab
         * 
         * @param tabTitle the title for the tab
         * @return return the LogPanel for the tab
         */
        protected LogPanel addTab(String tabTitle) {
            String name = tabTitle;
            if (tabTitle.indexOf(WekaTaskMap.s_taskNameIDSeparator) > 0) {
                String[] parts = name.split(WekaTaskMap.s_taskNameIDSeparator);
                name = parts[0];
            }

            LogPanel logP = new LogPanel();
            Dimension d2 = new Dimension(100, 170);
            logP.setPreferredSize(d2);
            logP.setMinimumSize(d2);

            m_monitorTabs.addTab(name, logP);
            int numTabs = m_monitorTabs.getTabCount();
            m_monitorTabs.setTabComponentAt(numTabs - 1, new CloseableTabTitle(m_monitorTabs));
            m_monitorTabs.setToolTipTextAt(numTabs - 1, tabTitle);

            return logP;
        }

        /**
         * Start monitoring a task
         * 
         * @param taskName the ID of the task to start monitoring
         */
        public synchronized void monitorTask(final String taskName) {

            final LogPanel theLog = addTab(taskName);

            Thread monitorThread = new Thread() {
                @Override
                public void run() {
                    Schedule sched = getScheduleForTask(taskName);

                    InputStream is = null;
                    PostMethod post = null;
                    TaskStatusInfo resultInfo = null;
                    // Get HTTP client
                    HttpClient client = WekaServer.ConnectionManager.getSingleton().createHttpClient();
                    WekaServer.ConnectionManager.addCredentials(client, m_username, m_password);
                    boolean ok = true;
                    try {
                        while (ok) {

                            if (this.isInterrupted()) {
                                break;
                            }

                            if (sched != null) {
                                Date now = new Date();
                                if (now.before(sched.getStartDate())) {
                                    // sleep until this task's start time
                                    sleep(sched.getStartDate().getTime() - now.getTime());
                                } else {
                                    sleep(m_monitorInterval * 1000);
                                }
                            } else {
                                sleep(m_monitorInterval * 1000);
                            }

                            String service = GetTaskStatusServlet.CONTEXT_PATH + "/?name=" + taskName + "&client=Y";
                            post = new PostMethod(constructURL(service));
                            post.setDoAuthentication(true);
                            post.addRequestHeader(new Header("Content-Type", "text/plain"));
                            int result = client.executeMethod(post);
                            // System.out.println("[WekaServer] Response from master server : "
                            // + result);
                            if (result == 401) {
                                theLog.statusMessage(
                                        "[Remote Execution]|ERROR: authentication " + "required on server.");
                                theLog.logMessage("Unable to get remote status of task'" + taskName
                                        + "' - authentication required.\n");
                                ok = false;
                            } else {
                                // the response
                                is = post.getResponseBodyAsStream();
                                ObjectInputStream ois = new ObjectInputStream(
                                        new BufferedInputStream(new GZIPInputStream(is)));
                                Object response = ois.readObject();
                                if (response.toString().startsWith(WekaServlet.RESPONSE_ERROR)) {
                                    theLog.statusMessage("[Remote Excecution]|ERROR: server "
                                            + "returned an error - see log (and/or log on server)");
                                    theLog.logMessage("Server returned an error: " + "for task : '" + taskName
                                            + "'. (" + response.toString() + ")." + " Check logs on server.");
                                    ok = false;
                                } else {
                                    if (response instanceof TaskStatusInfo) {
                                        resultInfo = ((TaskStatusInfo) response);
                                        if (resultInfo.getExecutionStatus() == TaskStatusInfo.FAILED) {
                                            ok = false;
                                            String message = resultInfo.getStatusMessage();
                                            theLog.statusMessage("[Remote Excecution]|ERROR: task "
                                                    + "execution failed - see log (and/or log on server)");
                                            if (message != null && message.length() > 0) {
                                                theLog.logMessage(message);
                                            }
                                        } else {
                                            String message = resultInfo.getStatusMessage();
                                            if (message != null && message.length() > 0
                                                    && !message.equals("New Task")) {
                                                BufferedReader br = new BufferedReader(new StringReader(message));
                                                String line = null;
                                                boolean statusMessages = true;
                                                while ((line = br.readLine()) != null) {
                                                    if (line.startsWith("@@@ Status messages")) {
                                                        statusMessages = true;
                                                    } else if (line.startsWith("@@@ Log messages")) {
                                                        statusMessages = false;
                                                    } else {
                                                        if (line.length() > 2) {
                                                            if (statusMessages) {
                                                                theLog.statusMessage(line);
                                                            } else {
                                                                theLog.logMessage(line);
                                                            }
                                                        }
                                                    }
                                                }
                                                br.close();

                                                // if the task is scheduled, and has an end time, only
                                                // continue to monitor if we haven't passed the end time
                                                if (sched != null && sched.getEndDate() != null) {
                                                    Date now = new Date();
                                                    if (now.after(sched.getEndDate())) {
                                                        ok = false;
                                                    }
                                                }
                                            }
                                            if (resultInfo.getExecutionStatus() == TaskStatusInfo.FINISHED
                                                    && sched == null) {
                                                // It's finished - no need to grab any more
                                                // logging/status
                                                // info, unless this is a scheduled task...
                                                ok = false;
                                                TaskStatusInfo taskResult = getResultFromServer(taskName);

                                                // see if there are any results...
                                                if (taskResult != null && taskResult.getTaskResult() != null) {

                                                    if (taskResult.getTaskResult() != null
                                                            && taskResult.getTaskResult() instanceof Map) {
                                                        Map<String, List<EventObject>> results = (Map<String, List<EventObject>>) taskResult
                                                                .getTaskResult();

                                                        String justName = taskName
                                                                .replace(FlowRunnerRemote.NAME_PREFIX, "");
                                                        justName = justName.substring(0, justName
                                                                .indexOf(WekaTaskMap.s_taskNameIDSeparator));
                                                        // System.out.println("Looking for tab: " +
                                                        // justName);
                                                        int flowIndex = -1;
                                                        for (int i = 0; i < m_mainPerspective.getNumTabs(); i++) {
                                                            if (m_mainPerspective.getTabTitle(i).equals(justName)) {
                                                                flowIndex = i;
                                                                break;
                                                            }
                                                        }
                                                        if (flowIndex >= 0) {
                                                            Vector beans = BeanInstance.getBeanInstances(flowIndex);
                                                            if (beans != null) {
                                                                for (int i = 0; i < beans.size(); i++) {
                                                                    BeanInstance temp = (BeanInstance) beans.get(i);
                                                                    if (temp.getBean() instanceof HeadlessEventCollector
                                                                            && temp.getBean() instanceof BeanCommon) {
                                                                        String beanName = ((BeanCommon) temp
                                                                                .getBean()).getCustomName();
                                                                        // System.out.println("Looking for " +
                                                                        // beanName + " in result Map");
                                                                        if (results.containsKey(beanName)) {
                                                                            // System.out.println("Processing headless events......");
                                                                            List<EventObject> headlessEvents = results
                                                                                    .get(beanName);
                                                                            ((HeadlessEventCollector) temp
                                                                                    .getBean())
                                                                                            .processHeadlessEvents(
                                                                                                    headlessEvents);
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    } else {
                                        theLog.logMessage("A problem occurred while "
                                                + "trying to retrieve remote status of task : '" + taskName
                                                + "'. Check logs on server.");
                                        theLog.statusMessage("[Remote Excecution]|ERROR: task "
                                                + "failed to return the correct task status object type"
                                                + " - see log (and/or log on server)");
                                        ok = false;
                                    }
                                }
                            }

                            if (is != null) {
                                try {
                                    is.close();
                                    is = null;
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }

                            if (post != null) {
                                post.releaseConnection();
                                post = null;
                            }
                        }
                    } catch (Exception ex) {
                        ex.printStackTrace();
                        theLog.statusMessage("[Remote Excecution]|ERROR: a error "
                                + "occurred while trying to retrieve remote status of task"
                                + " - see log (and/or log on server)");
                        theLog.logMessage(
                                "A problem occurred while " + "trying to retrieve remote status of task : '"
                                        + taskName + "' (" + ex.getMessage() + ")");

                    } finally {
                        if (is != null) {
                            try {
                                is.close();
                                is = null;
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }

                        if (post != null) {
                            post.releaseConnection();
                            post = null;
                        }
                    }
                }
            };

            ((CloseableTabTitle) m_monitorTabs.getTabComponentAt(m_monitorTabs.getTabCount() - 1))
                    .setMonitorThread(monitorThread);
            monitorThread.setPriority(Thread.MIN_PRIORITY);
            monitorThread.start();
        }
    }

    /** A reference to the main KF perspective */
    protected KnowledgeFlowApp.MainKFPerspective m_mainPerspective;

    /** A panel for configuring hostname and port */
    protected HostPanel m_hostPanel = new HostPanel();

    /** A panel for configuring a schedule */
    protected SchedulePanel m_schedulePanel = new SchedulePanel();

    /** A panel for initiating monitoring of task(s) */
    protected MonitorPanel m_monitorPanel = new MonitorPanel();

    /** Username for authenticating with the server (if necessary) */
    protected String m_username;

    /** Password for authenticating with the server (if necessary) */
    protected String m_password;

    /**
     * Construct the perspective
     */
    public KnowledgeFlowRemoteSchedulerPerspective() {
        setLayout(new BorderLayout());

        JPanel holderPanel = new JPanel();
        holderPanel.setLayout(new BorderLayout());
        holderPanel.add(m_hostPanel, BorderLayout.NORTH);
        holderPanel.add(m_schedulePanel, BorderLayout.SOUTH);

        add(holderPanel, BorderLayout.NORTH);
        add(m_monitorPanel, BorderLayout.CENTER);

        String wekaServerPasswordPath = WekaPackageManager.WEKA_HOME.toString() + File.separator + "server"
                + File.separator + "weka.pwd";
        File wekaServerPasswordFile = new File(wekaServerPasswordPath);
        boolean useAuth = wekaServerPasswordFile.exists();

        if (useAuth) {
            BufferedReader br = null;
            try {
                br = new BufferedReader(new FileReader(wekaServerPasswordFile));
                String line = null;
                while ((line = br.readLine()) != null) {
                    // not a comment character, then assume its the data
                    if (!line.startsWith("#")) {

                        String[] parts = line.split(":");
                        if (parts.length > 3 || parts.length < 2) {
                            continue;
                        }
                        m_username = parts[0].trim();
                        m_password = parts[1].trim();
                        if (parts.length == 3 && parts[1].trim().startsWith("OBF")) {
                            m_password = m_password + ":" + parts[2];
                            String deObbs = Password.deobfuscate(m_password);
                            m_password = deObbs;
                        }
                        break;
                    }
                }
            } catch (Exception ex) {
                System.err.println("[SchedulePerspective] Error reading " + "password file: " + ex.getMessage());
            } finally {
                if (br != null) {
                    try {
                        br.close();
                    } catch (Exception e) {
                    }
                }
            }
        }
    }

    protected String constructURL(String serviceAndArguments) {
        String realHostname = m_hostPanel.getHostName();
        String realPort = m_hostPanel.getPort();
        try {
            realHostname = Environment.getSystemWide().substitute(m_hostPanel.getHostName());
            realPort = Environment.getSystemWide().substitute(m_hostPanel.getPort());
        } catch (Exception ex) {
        }

        if (realPort.equals("80")) {
            realPort = "";
        } else {
            realPort = ":" + realPort;
        }

        String retVal = "http://" + realHostname + realPort + serviceAndArguments;

        retVal = retVal.replace(" ", "%20");

        return retVal;
    }

    /**
     * Returns true if this perspective can accept instances from the main
     * perspective
     * 
     * @return false - this perspective does not use/require instances
     */
    @Override
    public boolean acceptsInstances() {
        return false;
    }

    /**
     * Get the Icon for this perspective
     * 
     * @return the icon for this perspective
     */
    @Override
    public Icon getPerspectiveIcon() {
        java.awt.Image pic = null;
        java.net.URL imageURL = this.getClass().getClassLoader().getResource("weka/gui/beans/icons/calendar.png");

        if (imageURL == null) {
            return null;
        } else {
            pic = java.awt.Toolkit.getDefaultToolkit().getImage(imageURL);
        }
        return new javax.swing.ImageIcon(pic);
    }

    /**
     * Get the tool tip text for this perspective.
     * 
     * @return the tool tip text for this perspective
     */
    @Override
    public String getPerspectiveTipText() {
        return "Schedule knowledge flows to run on remote hosts";
    }

    /**
     * Get the title of this perspective
     * 
     * @return the title of this perspective
     */
    @Override
    public String getPerspectiveTitle() {
        return "Remote host scheduler";
    }

    /**
     * Set active status of this perspective. True indicates that this perspective
     * is the visible active perspective in the KnowledgeFlow
     * 
     * @param active true if this perspective is the active one
     */
    @Override
    public void setActive(boolean active) {
    }

    /**
     * Set instances (if the perspective accepts them)
     * 
     * @param insts the instances
     */
    @Override
    public void setInstances(Instances insts) throws Exception {
        // Nothing to do
    }

    /**
     * Set whether this perspective is "loaded" - i.e. whether or not the user has
     * opted to have it available in the perspective toolbar. The perspective can
     * make the decision as to allocating or freeing resources on the basis of
     * this.
     * 
     * @param loaded true if the perspective is available in the perspective
     *          toolbar of the KnowledgeFlow
     */
    @Override
    public void setLoaded(boolean loaded) {
    }

    /**
     * Set a reference to the main KnowledgeFlow perspective - i.e. the
     * perspective that manages flow layouts.
     * 
     * @param mainPerspective the main KnowledgeFlow perspective.
     */
    @Override
    public void setMainKFPerspective(MainKFPerspective mainPerspective) {
        m_mainPerspective = mainPerspective;
    }

    /**
     * Main method for testing this class
     * 
     * @param args command line arguments
     */
    public static void main(String[] args) {
        final javax.swing.JFrame jf = new javax.swing.JFrame();
        jf.getContentPane().setLayout(new java.awt.BorderLayout());
        KnowledgeFlowRemoteSchedulerPerspective p = new KnowledgeFlowRemoteSchedulerPerspective();

        jf.getContentPane().add(p, BorderLayout.CENTER);
        jf.addWindowListener(new java.awt.event.WindowAdapter() {
            @Override
            public void windowClosing(java.awt.event.WindowEvent e) {
                jf.dispose();
                System.exit(0);
            }
        });
        jf.setSize(800, 600);
        jf.setVisible(true);
    }
}