nz.govt.natlib.ndha.manualdeposit.jobmanagement.JobQueueManagement.java Source code

Java tutorial

Introduction

Here is the source code for nz.govt.natlib.ndha.manualdeposit.jobmanagement.JobQueueManagement.java

Source

/**
 * Software License
 *
 * Copyright 2007/2010 National Library of New Zealand.
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0 
 *
 * or the file "LICENSE.txt" included with the software.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 *
 */

package nz.govt.natlib.ndha.manualdeposit.jobmanagement;

import java.awt.event.ActionEvent;
import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTable;
import javax.swing.Timer;

import nz.govt.natlib.ndha.manualdeposit.AppProperties;
import nz.govt.natlib.ndha.manualdeposit.IManualDepositMainFrame;
import nz.govt.natlib.ndha.manualdeposit.exceptions.JobQueueException;
import nz.govt.natlib.ndha.manualdeposit.metadata.PersonalSettings;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author PlayerM
 * 
 */
public final class JobQueueManagement {

    private final static Log LOG = LogFactory.getLog(JobQueueManagement.class);
    private final AppProperties theAppProperties;
    private final PersonalSettings personalSettings;
    private final IManualDepositMainFrame mainFrame;
    private final List<UploadJob> jobQueueRunning = new ArrayList<UploadJob>();
    private final List<UploadJob> jobQueuePending = new ArrayList<UploadJob>();
    private final List<UploadJob> jobQueueFailed = new ArrayList<UploadJob>();
    private final List<UploadJob> jobQueueDeposited = new ArrayList<UploadJob>();
    private final List<UploadJob> jobQueueInPermanent = new ArrayList<UploadJob>();
    private final List<UploadJob> jobQueueAwaitingCleanup = new ArrayList<UploadJob>();
    private final JTable theJobQueueRunningTable;
    private final JTable theJobQueuePendingTable;
    private final JTable theJobQueueFailedTable;
    private final JTable theJobQueueDepositedTable;
    private final JTable theJobQueueInPermanentTable;
    private JTable theTableSelected = null;

    public static JobQueueManagement create(final AppProperties appProperties, final IManualDepositMainFrame frame,
            final JTable jobQueueRunningTable, final JTable jobQueuePendingTable, final JTable jobQueueFailedTable,
            final JTable jobQueueDepositedTable, final JTable jobQueueCompleteTable) throws Exception {
        return new JobQueueManagement(appProperties, frame, jobQueueRunningTable, jobQueuePendingTable,
                jobQueueFailedTable, jobQueueDepositedTable, jobQueueCompleteTable);
    }

    public JobQueueManagement(final AppProperties appProperties, final IManualDepositMainFrame frame,
            final JTable jobQueueRunningTable, final JTable jobQueuePendingTable, final JTable jobQueueFailedTable,
            final JTable jobQueueDepositedTable, final JTable jobQueueCompleteTable) throws Exception {
        theAppProperties = appProperties;
        personalSettings = theAppProperties.getApplicationData().getPersonalSettings();
        mainFrame = frame;
        theJobQueueRunningTable = jobQueueRunningTable;
        theJobQueuePendingTable = jobQueuePendingTable;
        theJobQueueFailedTable = jobQueueFailedTable;
        theJobQueueDepositedTable = jobQueueDepositedTable;
        theJobQueueInPermanentTable = jobQueueCompleteTable;
        loadJobs();
        refreshJobQueue();
    }

    private final FileFilter xmlFilter = new FileFilter() {
        public boolean accept(final File file) {
            return file.isFile() && file.getName().toLowerCase().endsWith(".xml");
        }
    };

    private void loadJobs() throws JobQueueException {
        LOG.debug("Start loadJobs");
        final File jobQueueFile = new File(theAppProperties.getApplicationData().getJobQueuePath());
        if (jobQueueFile.exists() && jobQueueFile.isDirectory()) {
            final File[] files = jobQueueFile.listFiles(xmlFilter);
            for (File file : files) {
                try {
                    LOG.debug("Loading job " + file.getAbsolutePath());
                    final UploadJob job = new UploadJob(file.getPath(), theAppProperties);
                    LOG.debug("Job loaded, adding to queue " + job.getJobDetail().get(0).getEntityName());
                    switch (job.getJobState()) {
                    case Running:
                        job.resubmitJob();
                    case Pending:
                    case Requested:
                    case Batching:
                        jobQueuePending.add(job);
                        break;
                    case Failed:
                        jobQueueFailed.add(job);
                        break;
                    case Deposited:
                        jobQueueDeposited.add(job);
                        break;
                    case AwaitingCleanup:
                        jobQueueAwaitingCleanup.add(job);
                    case Complete:
                        break;
                    default:
                        break;
                    }
                } catch (Exception ex) {
                    final String message = String.format("The Job %s Cannot be loaded\n %s", file.getName(),
                            ex.getMessage());
                    LOG.error(ex.getMessage(), ex);
                    LOG.error("Printing contents of Job Queue folder...");
                    saveJobQueueContentsToLOG(jobQueueFile);

                    throw new JobQueueException("Cannot load job - " + message);
                }
            }
        } else {
            final String message = "The Job Queue directory ("
                    + theAppProperties.getApplicationData().getJobQueuePath()
                    + ") must exist and must be a directory";
            throw new JobQueueException("Cannot run - " + message);
        }
        refreshJobQueue();
        checkJobQueue();
        checkSipStatus();
        LOG.debug("End loadJobs");
    }

    // Created 23.Jan.2014 to help identify job queue error
    private void saveJobQueueContentsToLOG(File file) {
        for (File childFile : file.listFiles()) {

            LOG.error(childFile.getAbsolutePath());
            if (childFile.isDirectory()) {
                saveJobQueueContentsToLOG(childFile);
            }
        }
    }

    private boolean checkAndMoveJobs(final List<UploadJob> jobsToCheck) {
        LOG.debug("Start checkAndMoveJobs");
        boolean itemsMoved = false;
        for (int i = 0; i < jobsToCheck.size(); i++) {
            UploadJob job = jobsToCheck.get(i);
            if (jobsToCheck.equals(jobQueueAwaitingCleanup)) {
                if (job.getJobState() != UploadJob.JobState.AwaitingCleanup) {
                    jobsToCheck.remove(job);
                    itemsMoved = true;
                }
            } else {
                switch (job.getJobState()) {
                case Running:
                    if (!jobsToCheck.equals(jobQueueRunning)) {
                        jobsToCheck.remove(job);
                        jobQueueRunning.add(job);
                        itemsMoved = true;
                    }
                    break;
                case Requested:
                case Pending:
                case Batching:
                    if (!jobsToCheck.equals(jobQueuePending)) {
                        jobsToCheck.remove(job);
                        jobQueuePending.add(job);
                        itemsMoved = true;
                    }
                    break;
                case Failed:
                    if (!jobsToCheck.equals(jobQueueFailed)) {
                        jobsToCheck.remove(job);
                        jobQueueFailed.add(job);
                        itemsMoved = true;
                    }
                    break;
                case Deposited:
                    if (!jobsToCheck.equals(jobQueueDeposited)) {
                        jobsToCheck.remove(job);
                        jobQueueDeposited.add(job);
                        itemsMoved = true;
                    }
                    break;
                case AwaitingCleanup:
                case Complete:
                    if ((jobsToCheck != jobQueueInPermanent) && (jobsToCheck != jobQueueAwaitingCleanup)) {
                        jobsToCheck.remove(job);
                        if (job.getJobState() == UploadJob.JobState.AwaitingCleanup) {
                            jobQueueInPermanent.add(job);
                        }
                        jobQueueAwaitingCleanup.add(job);
                        itemsMoved = true;
                    }
                    break;
                default:
                    break;
                }
            }
        }
        LOG.debug("End checkAndMoveJobs");
        return itemsMoved;
    }

    private void checkJobQueue() {
        LOG.debug("Start checkJobQueue");
        boolean itemsMoved = checkAndMoveJobs(jobQueueRunning) || checkAndMoveJobs(jobQueuePending)
                || checkAndMoveJobs(jobQueueFailed) || checkAndMoveJobs(jobQueueDeposited)
                || checkAndMoveJobs(jobQueueInPermanent) || checkAndMoveJobs(jobQueueAwaitingCleanup);
        while ((!jobQueuePending.isEmpty())
                && (jobQueueRunning.size() < theAppProperties.getApplicationData().getMaximumJobsRunning())) {
            UploadJob job;
            // need to take either the first or last depending on whether the
            // queue is sorted Asc or Desc
            int jobNumber;
            if (personalSettings.isSortPendingAscending()) {
                jobNumber = 0;
            } else {
                jobNumber = jobQueuePending.size() - 1;
            }
            job = jobQueuePending.get(jobNumber);
            while (job.isCreatingCopy() && jobNumber >= 0 && jobNumber < jobQueuePending.size()) {
                if (personalSettings.isSortPendingAscending()) {
                    jobNumber++;
                } else {
                    jobNumber--;
                }
                if (jobNumber >= 0 && jobNumber < jobQueuePending.size()) {
                    job = jobQueuePending.get(jobNumber);
                }
            }
            if (job.isCreatingCopy()) {
                break;
            }
            if (job.lock()) {
                jobQueuePending.remove(job);
                jobQueueRunning.add(job);
                itemsMoved = true;
                Thread t = new Thread(job);
                t.start();
                job.unlock();
            } else {
                LOG.debug("Couldn't lock job " + job.getJobDetail().get(0).getEntityName());
                try {
                    Thread.sleep(100);
                } catch (Exception ex) {
                }
            }
        }
        if (itemsMoved) {
            refreshJobQueue();
        }
        if (theJobQueuePendingTable != null) {
            theJobQueuePendingTable.repaint();
        }
        if (theJobQueueRunningTable != null) {
            theJobQueueRunningTable.repaint();
        }
        if (theJobQueueFailedTable != null) {
            theJobQueueFailedTable.repaint();
        }
        if (theJobQueueDepositedTable != null) {
            theJobQueueDepositedTable.repaint();
        }
        if (theJobQueueInPermanentTable != null) {
            theJobQueueInPermanentTable.repaint();
        }
        final Action checkJobQueueAction = new AbstractAction() {
            private static final long serialVersionUID = 5562669711772031634L;

            public void actionPerformed(final ActionEvent e) {
                Timer t = (Timer) e.getSource();
                t.stop();
                checkJobQueue();
            }
        };
        new Timer(theAppProperties.getApplicationData().getJobQueueRefreshInterval(), checkJobQueueAction).start();
        LOG.debug("End checkJobQueue");
    }

    private void checkSipStatus() {
        LOG.debug("Start checkSipStatus");
        for (int i = jobQueueDeposited.size() - 1; i >= 0; i--) {
            final UploadJob job = jobQueueDeposited.get(i);
            job.checkSipStatus();
        }
        for (int i = jobQueueAwaitingCleanup.size() - 1; i >= 0; i--) {
            final UploadJob job = jobQueueAwaitingCleanup.get(i);
            job.checkForCleanup();
        }
        final Action checkSipStatusAction = new AbstractAction() {
            private static final long serialVersionUID = -8315654343127184873L;

            public void actionPerformed(final ActionEvent e) {
                Timer t = (Timer) e.getSource();
                t.stop();
                checkSipStatus();
            }
        };
        new Timer(theAppProperties.getApplicationData().getSipStatusRefreshInterval(), checkSipStatusAction)
                .start();
        LOG.debug("End checkSipStatus");
    }

    private void resortTable(final java.awt.event.ActionEvent evt) {
        if (theTableSelected.equals(theJobQueueRunningTable)) {
            personalSettings.setSortRunningAscending(!personalSettings.isSortRunningAscending());
        } else if (theTableSelected.equals(theJobQueuePendingTable)) {
            personalSettings.setSortPendingAscending(!personalSettings.isSortPendingAscending());
        } else if (theTableSelected.equals(theJobQueueFailedTable)) {
            personalSettings.setSortFailedAscending(!personalSettings.isSortFailedAscending());
        } else if (theTableSelected.equals(theJobQueueDepositedTable)) {
            personalSettings.setSortDepositedAscending(!personalSettings.isSortDepositedAscending());
        } else if (theTableSelected.equals(theJobQueueInPermanentTable)) {
            personalSettings.setSortCompleteAscending(!personalSettings.isSortCompleteAscending());
        }
        refreshJobQueue();
    }

    public UploadJob getUploadJob(final String jobName) {
        UploadJob result = null;
        for (UploadJob job : jobQueueRunning) {
            if (job.getBatchName().equalsIgnoreCase(jobName)) {
                result = job;
                break;
            }
        }
        if (result == null) {
            for (UploadJob job : jobQueuePending) {
                if (job.getBatchName().equalsIgnoreCase(jobName)) {
                    result = job;
                    break;
                }
            }
        }
        if (result == null) {
            for (UploadJob job : jobQueueFailed) {
                if (job.getBatchName().equalsIgnoreCase(jobName)) {
                    result = job;
                    break;
                }
            }
        }
        if (result == null) {
            for (UploadJob job : jobQueueDeposited) {
                if (job.getBatchName().equalsIgnoreCase(jobName)) {
                    result = job;
                    break;
                }
            }
        }
        if (result == null) {
            for (UploadJob job : jobQueueInPermanent) {
                if (job.getBatchName().equalsIgnoreCase(jobName)) {
                    result = job;
                    break;
                }
            }
        }
        if (result == null) {
            for (UploadJob job : jobQueueAwaitingCleanup) {
                if (job.getBatchName().equalsIgnoreCase(jobName)) {
                    result = job;
                    break;
                }
            }
        }
        return result;
    }

    public JPopupMenu getJobQueueMenu(final JTable table) {
        theTableSelected = table;
        final JPopupMenu menu = new JPopupMenu();
        boolean sortAscending = false;
        if (table.equals(theJobQueueRunningTable)) {
            sortAscending = personalSettings.isSortRunningAscending();
        } else if (table.equals(theJobQueuePendingTable)) {
            sortAscending = personalSettings.isSortPendingAscending();
        } else if (table.equals(theJobQueueFailedTable)) {
            sortAscending = personalSettings.isSortFailedAscending();
        } else if (table.equals(theJobQueueDepositedTable)) {
            sortAscending = personalSettings.isSortDepositedAscending();
        } else if (table.equals(theJobQueueInPermanentTable)) {
            sortAscending = personalSettings.isSortCompleteAscending();
        }
        JMenuItem item;
        if (sortAscending) {
            item = new JMenuItem("Sort by date in descending order");
        } else {
            item = new JMenuItem("Sort by date in ascending order");
        }
        item.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(final java.awt.event.ActionEvent evt) {
                resortTable(evt);
            }
        });
        menu.add(item);
        if ((table.equals(theJobQueueRunningTable)) || (table.equals(theJobQueuePendingTable))
                || (table.equals(theJobQueueFailedTable))) {
            StringBuffer text = new StringBuffer();
            text.append("Cancel job");
            if (table.getSelectedRowCount() > 1) {
                text.append('s');
            }
            item = new JMenuItem(text.toString());
            item.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(final java.awt.event.ActionEvent evt) {
                    try {
                        cancelJobs(evt);
                    } catch (JobQueueException ex) {
                        String message = "Error cancelling jobs";
                        LOG.error(message, ex);
                        mainFrame.showError("An error occurred", message, ex);
                    }
                }
            });
            menu.add(item);
            if (table.equals(theJobQueueFailedTable)) {
                text = new StringBuffer();
                text.append("Resubmit job");
                if (table.getSelectedRowCount() > 1) {
                    text.append('s');
                }
                item = new JMenuItem(text.toString());
                item.addActionListener(new java.awt.event.ActionListener() {
                    public void actionPerformed(final java.awt.event.ActionEvent evt) {
                        resubmitJob(evt);
                    }
                });
                menu.add(item);
            }
        }
        if (table.equals(theJobQueueInPermanentTable)) {
            item = new JMenuItem("Clear completed jobs");
            item.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(final java.awt.event.ActionEvent evt) {
                    clearFinishedJobs(evt);
                }
            });
            menu.add(item);
        }
        if (menu == null || menu.getComponentCount() == 0) {
            return null;
        } else {
            return menu;
        }
    }

    private void clearFinishedJobs(final java.awt.event.ActionEvent evt) {
        while (!jobQueueInPermanent.isEmpty()) {
            final UploadJob job = jobQueueInPermanent.get(0);
            final JobQueueTableModel model = (JobQueueTableModel) theJobQueueInPermanentTable.getModel();
            model.removeJob(job);
            jobQueueInPermanent.remove(job);
        }
        theJobQueueInPermanentTable.repaint();
    }

    private void resubmitJob(final java.awt.event.ActionEvent evt) {
        final JobQueueTableModel model = (JobQueueTableModel) theTableSelected.getModel();
        for (int i = theTableSelected.getSelectedRows().length - 1; i >= 0; i--) {
            int selectedRow = theTableSelected.getSelectedRows()[i];
            UploadJob job = model.getRow(selectedRow);
            boolean submitJob = true;
            if (job.getSIPID() != null && (!job.getSIPID().equals(""))) {
                String message = String.format(
                        "Job %s has a SIP ID (%s) implying that it has already been submitted.\nAre you sure you want to resubmit this job?",
                        job.getJobDetail().get(0).getEntityName(), job.getSIPID());
                submitJob = mainFrame.confirm(message, true);
            }
            if (submitJob) {
                job.resubmitJob();
            }
        }
    }

    private class CancelJobs implements Runnable {

        private final Lock _cancelJobsLock = new ReentrantLock();

        public boolean lock() {
            return _cancelJobsLock.tryLock();
        }

        public void unlock() {
            _cancelJobsLock.unlock();
        }

        public void run() {
            if (mainFrame != null) {
                if (theTableSelected.getSelectedRows().length == 0) {
                    mainFrame.showError("Could not cancel job", "There are no selected jobs available to cancel");
                    return;
                }
                String message;
                if (theTableSelected.getSelectedRows().length > 1) {
                    message = "Cancel jobs?";
                } else {
                    message = "Cancel job?";
                }
                if (mainFrame.confirm(message, true)) {
                    JobQueueTableModel model = (JobQueueTableModel) theTableSelected.getModel();
                    for (int i = theTableSelected.getSelectedRows().length - 1; i >= 0; i--) {
                        int selectedRow = theTableSelected.getSelectedRows()[i];
                        UploadJob job = model.getRow(selectedRow);
                        if (job != null) {
                            if ((job.getJobState() != UploadJob.JobState.Deposited)
                                    || (job.getJobState() != UploadJob.JobState.Complete)) {
                                job.cancelJob();
                                if (jobQueueRunning.contains(job)) {
                                    jobQueueRunning.remove(job);
                                } else if (jobQueuePending.contains(job)) {
                                    jobQueuePending.remove(job);
                                } else if (jobQueueFailed.contains(job)) {
                                    jobQueueFailed.remove(job);
                                }
                                model.removeJob(job);
                            }
                        }
                    }
                }
            }
        }

    }

    private void cancelJobs(final java.awt.event.ActionEvent evt) throws JobQueueException {
        CancelJobs cancelJobs = new CancelJobs();
        if (cancelJobs.lock()) {
            Thread t = new Thread(cancelJobs);
            t.start();
            cancelJobs.unlock();
        } else {
            LOG.debug("Couldn't cancel jobs");
            throw new JobQueueException("Couldn't cancel jobs");
        }
    }

    public void addJob(final UploadJob job) {
        LOG.debug("Start addJob");
        job.lock();
        JobQueueTableModel model = (JobQueueTableModel) theJobQueuePendingTable.getModel();
        jobQueuePending.add(job);
        Collections.sort(jobQueuePending, new UploadJobComparator(personalSettings));
        if ((jobQueuePending.size() == 1) || (personalSettings.isSortPendingAscending())) {
            model.addRow(job);
        } else {
            model.insertJob(job, jobQueuePending.get(0), true);
        }
        job.unlock();
        refreshJobQueue();
        LOG.debug("End addJob");
    }

    private void sortQueues() {
        Collections.sort(jobQueueRunning, new UploadJobComparator(personalSettings));
        Collections.sort(jobQueuePending, new UploadJobComparator(personalSettings));
        Collections.sort(jobQueueFailed, new UploadJobComparator(personalSettings));
        Collections.sort(jobQueueDeposited, new UploadJobComparator(personalSettings));
        Collections.sort(jobQueueInPermanent, new UploadJobComparator(personalSettings));
    }

    public void refreshJobQueue() {
        LOG.debug("Start refreshJobQueue");
        sortQueues();
        JobQueueTableModel model = (JobQueueTableModel) theJobQueueRunningTable.getModel();
        model.clearTable();
        for (int i = 0; i < jobQueueRunning.size(); i++) {
            UploadJob job = jobQueueRunning.get(i);
            model.addRow(job);
        }
        theJobQueueRunningTable.repaint();

        model = (JobQueueTableModel) theJobQueuePendingTable.getModel();
        model.clearTable();
        for (int i = 0; i < jobQueuePending.size(); i++) {
            UploadJob job = jobQueuePending.get(i);
            model.addRow(job);
        }
        theJobQueuePendingTable.repaint();

        model = (JobQueueTableModel) theJobQueueFailedTable.getModel();
        model.clearTable();
        for (int i = 0; i < jobQueueFailed.size(); i++) {
            UploadJob job = jobQueueFailed.get(i);
            model.addRow(job);
        }
        theJobQueueFailedTable.repaint();

        model = (JobQueueTableModel) theJobQueueDepositedTable.getModel();
        model.clearTable();
        for (int i = 0; i < jobQueueDeposited.size(); i++) {
            UploadJob job = jobQueueDeposited.get(i);
            model.addRow(job);
        }
        theJobQueueDepositedTable.repaint();

        model = (JobQueueTableModel) this.theJobQueueInPermanentTable.getModel();
        model.clearTable();
        for (int i = 0; i < jobQueueInPermanent.size(); i++) {
            UploadJob job = jobQueueInPermanent.get(i);
            model.addRow(job);
        }
        theJobQueueInPermanentTable.repaint();
        LOG.debug("End refreshJobQueue");
    }

    public boolean jobsOutstanding() {
        int noOfJobs = jobQueueRunning.size() + jobQueuePending.size() + jobQueueFailed.size()
                + jobQueueDeposited.size();
        return (noOfJobs > 0);
    }

    public boolean jobsRunning() {
        int noOfJobs = jobQueueRunning.size() + jobQueuePending.size();
        return (noOfJobs > 0);
    }
}