001/*
002 * #%L
003 * Netarchivesuite - harvester
004 * %%
005 * Copyright (C) 2005 - 2014 The Royal Danish Library, the Danish State and University Library,
006 *             the National Library of France and the Austrian National Library.
007 * %%
008 * This program is free software: you can redistribute it and/or modify
009 * it under the terms of the GNU Lesser General Public License as
010 * published by the Free Software Foundation, either version 2.1 of the
011 * License, or (at your option) any later version.
012 * 
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Lesser Public License for more details.
017 * 
018 * You should have received a copy of the GNU General Lesser Public
019 * License along with this program.  If not, see
020 * <http://www.gnu.org/licenses/lgpl-2.1.html>.
021 * #L%
022 */
023
024package dk.netarkivet.harvester.webinterface;
025
026import java.util.List;
027
028import javax.servlet.jsp.PageContext;
029
030
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033
034import dk.netarkivet.common.exceptions.ArgumentNotValid;
035import dk.netarkivet.common.exceptions.ForwardedToErrorPage;
036import dk.netarkivet.common.exceptions.IOFailure;
037import dk.netarkivet.common.exceptions.UnknownID;
038import dk.netarkivet.common.utils.I18n;
039import dk.netarkivet.common.webinterface.HTMLUtils;
040import dk.netarkivet.harvester.datamodel.Job;
041import dk.netarkivet.harvester.datamodel.JobDAO;
042import dk.netarkivet.harvester.datamodel.JobStatus;
043import dk.netarkivet.harvester.datamodel.JobStatusInfo;
044import dk.netarkivet.harvester.webinterface.HarvestStatusQuery.UI_FIELD;
045
046
047/**
048 * This page provides support for the HarvestStatus pages of the web interface.
049 */
050public class HarvestStatus {
051
052    /** The logger to use. */
053    //protected static final Log log = LogFactory.getLog(HarvestStatus.class.getName());
054    protected static final Logger log = LoggerFactory.getLogger(HarvestStatus.class);
055
056    /** The total number in the full resultset. */
057    private final long fullResultsCount;
058
059    /** The list of jobs in this HarvestStatus object. */
060    private final List<JobStatusInfo> jobs;
061
062    /**
063     * Constructor for the HarvestStatus class.
064     *
065     * @param fullResultsCount The total number of entries in the full resultset
066     * @param jobs The list of jobs
067     */
068    public HarvestStatus(long fullResultsCount, List<JobStatusInfo> jobs) {
069        this.fullResultsCount = fullResultsCount;
070        this.jobs = jobs;
071    }
072
073    /**
074     * @return The total number in the full resultset
075     */
076    public long getFullResultsCount() {
077        return fullResultsCount;
078    }
079
080    /**
081     * @return The list of jobs in this HarvestStatus object.
082     */
083    public List<JobStatusInfo> getJobStatusInfo() {
084        return jobs;
085    }
086
087    /**
088     * Process a request from Harveststatus-alljobs.
089     * <p>
090     * Will resubmit a job if requested, otherwise do nothing.
091     *
092     * @param context The web context used for processing
093     * @param i18n The resource i18n context.
094     * @throws ForwardedToErrorPage If an error occurs that stops processing and forwards the user to an error page.
095     */
096    public static void processRequest(PageContext context, I18n i18n) throws ForwardedToErrorPage {
097        ArgumentNotValid.checkNotNull(context, "PageContext context");
098        ArgumentNotValid.checkNotNull(i18n, "I18n i18n");
099
100        // Check if it's a multiple resubmit query
101        String resubmitJobIds = UI_FIELD.RESUBMIT_JOB_IDS.getValue(context.getRequest());
102        Long resubmitJobID = HTMLUtils.parseOptionalLong(context, Constants.JOB_RESUBMIT_PARAM, null);
103        Long rejectJobID = HTMLUtils.parseOptionalLong(context, Constants.JOB_REJECT_PARAM, null);
104        Long unrejectJobID = HTMLUtils.parseOptionalLong(context, Constants.JOB_UNREJECT_PARAM, null);
105        if (!resubmitJobIds.isEmpty()) {
106            String[] ids = resubmitJobIds.split(";");
107            for (String idStr : ids) {
108                resubmitJob(context, i18n, Long.parseLong(idStr));
109            }
110        } else if (resubmitJobID != null) {
111            resubmitJob(context, i18n, resubmitJobID);
112        } else if (rejectJobID != null) {
113            rejectFailedJob(context, i18n, rejectJobID);
114        } else if (unrejectJobID != null) {
115            unrejectRejectedJob(context, i18n, unrejectJobID);
116        }
117    }
118
119    /**
120     * Marks a failed job as rejected for resubmission. Throws a ForwardedToErrorPage if jobID is null or if it refers
121     * to a job that is not in the state FAILED to start with.
122     *
123     * @param context the context for forwarding errors
124     * @param i18n the internationalisation to use
125     * @param jobID the job to reject
126     */
127    public static void rejectFailedJob(PageContext context, I18n i18n, Long jobID) {
128        try {
129            Job job = JobDAO.getInstance().read(jobID);
130            if (!job.getStatus().equals(JobStatus.FAILED)) {
131                HTMLUtils.forwardWithErrorMessage(context, i18n, "errormsg;job.unable.to.reject", jobID);
132                throw new ForwardedToErrorPage("Cannot reject job in status " + job.getStatus());
133            }
134            job.setStatus(JobStatus.FAILED_REJECTED);
135            JobDAO.getInstance().update(job);
136        } catch (ArgumentNotValid argumentNotValid) {
137            HTMLUtils.forwardOnEmptyParameter(context, "jobID");
138            throw new ForwardedToErrorPage("jobID parameter is null");
139        } catch (UnknownID unknownID) {
140            HTMLUtils.forwardWithErrorMessage(context, i18n, "errormsg;job.unknown.id.0", jobID);
141            throw new ForwardedToErrorPage("Job " + jobID + " not found");
142        } catch (IOFailure ioFailure) {
143            HTMLUtils.forwardWithErrorMessage(context, i18n, ioFailure, "errormsg;job.unable.to.reject", jobID);
144            throw new ForwardedToErrorPage("Error resubmitting job " + jobID);
145        }
146
147    }
148
149    /**
150     * Marks as failed. Throws a ForwardedToErrorPage if the job is not in the state FAILED_REJECTED to start with.
151     *
152     * @param context the context for forwarding errors
153     * @param i18n the internationalisation to use
154     * @param jobID the job to unreject
155     */
156    public static void unrejectRejectedJob(PageContext context, I18n i18n, Long jobID) {
157        try {
158            Job job = JobDAO.getInstance().read(jobID);
159            if (!job.getStatus().equals(JobStatus.FAILED_REJECTED)) {
160                HTMLUtils.forwardWithErrorMessage(context, i18n, "errormsg;job.unable.to.reject", jobID);
161                throw new ForwardedToErrorPage("Cannot unreject job in status " + job.getStatus());
162            }
163            job.setStatus(JobStatus.FAILED);
164            JobDAO.getInstance().update(job);
165        } catch (ArgumentNotValid argumentNotValid) {
166            HTMLUtils.forwardOnEmptyParameter(context, "jobID");
167            throw new ForwardedToErrorPage("jobID parameter is null");
168        } catch (UnknownID unknownID) {
169            HTMLUtils.forwardWithErrorMessage(context, i18n, "errormsg;job.unknown.id.0", jobID);
170            throw new ForwardedToErrorPage("Job " + jobID + " not found");
171        } catch (IOFailure ioFailure) {
172            HTMLUtils.forwardWithErrorMessage(context, i18n, ioFailure, "errormsg;job.unable.to.reject", jobID);
173            throw new ForwardedToErrorPage("Error resubmitting job " + jobID);
174        }
175    }
176
177    /**
178     * Helpermethod to resubmit a job with a given jobID.
179     *
180     * @param context the current pageContext (used in error-handling only)
181     * @param i18n the given internalisation object.
182     * @param jobID The ID for the job that we want to resubmit.
183     */
184    private static void resubmitJob(PageContext context, I18n i18n, Long jobID) {
185        try {
186            JobDAO.getInstance().rescheduleJob(jobID);
187        } catch (UnknownID e) {
188            HTMLUtils.forwardWithErrorMessage(context, i18n, "errormsg;job.unknown.id.0", jobID);
189            throw new ForwardedToErrorPage("Job " + jobID + " not found");
190        } catch (IOFailure e) {
191            HTMLUtils.forwardWithErrorMessage(context, i18n, e, "errormsg;job.unable.to.resubmit.id.0", jobID);
192            throw new ForwardedToErrorPage("Error resubmitting job " + jobID);
193        }
194    }
195
196    /**
197     * Create a link to the harvest-run page for a given run.
198     *
199     * @param harvestID The ID of the harvest
200     * @param harvestRun The run # of the harvest (always 0 for snapshots)
201     * @return A properly encoded HTML string with a link and the harvest run as the text. Select all jobs to be shown.
202     */
203    public static String makeHarvestRunLink(long harvestID, int harvestRun) {
204        ArgumentNotValid.checkNotNegative(harvestID, "harvestID");
205        ArgumentNotValid.checkNotNegative(harvestRun, "harvestRun");
206        return "<a href=\"/History/Harveststatus-perharvestrun.jsp?" + HarvestStatusQuery.UI_FIELD.HARVEST_ID.name()
207                + "=" + harvestID + "&amp;" + Constants.HARVEST_NUM_PARAM + "=" + harvestRun + "&amp;"
208                + HarvestStatusQuery.UI_FIELD.JOB_STATUS.name() + "=" + HarvestStatusQuery.JOBSTATUS_ALL + "\">"
209                + harvestRun + "</a>";
210    }
211
212    /**
213     * Calculate list of job information to be shown.
214     *
215     * @param query the query with its filters.
216     * @return a list of job status info objects
217     */
218    public static HarvestStatus getjobStatusList(HarvestStatusQuery query) {
219        log.debug("Getting a jobstatuslist based on the current query. ");
220        return JobDAO.getInstance().getStatusInfo(query);
221    }
222
223    /**
224     * Check if next link is active.
225     *
226     * @param pageSize the size of the page
227     * @param totalResultsCount the number of results.
228     * @param endIndex the index of the last result shown on this page
229     * @return true, if link to next page is active
230     */
231    public static boolean isNextLinkActive(long pageSize, long totalResultsCount, long endIndex) {
232        if (pageSize != 0 && totalResultsCount > 0 && endIndex < totalResultsCount) {
233            return true;
234        }
235        return false;
236    }
237
238    /**
239     * Check if previous link is active.
240     *
241     * @param pageSize the size of the page
242     * @param totalResultsCount the number of results.
243     * @param startIndex the index of the first result shown on this page
244     * @return true, if link to previous page is active
245     */
246    public static boolean isPreviousLinkActive(long pageSize, long totalResultsCount, long startIndex) {
247        if (pageSize != 0 && totalResultsCount > 0 && startIndex > 1) {
248            return true;
249        }
250        return false;
251    }
252}