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 */ 023package dk.netarkivet.harvester.webinterface; 024 025import java.text.ParseException; 026import java.text.SimpleDateFormat; 027import java.util.Date; 028import java.util.HashSet; 029import java.util.Set; 030 031import javax.servlet.ServletRequest; 032 033import dk.netarkivet.common.CommonSettings; 034import dk.netarkivet.common.exceptions.ArgumentNotValid; 035import dk.netarkivet.common.utils.Settings; 036import dk.netarkivet.harvester.datamodel.JobStatus; 037 038/** 039 * Represents a query for a set of jobs. Filtering can be performed on: 040 * <ul> 041 * <li>Job Status (multiple)</li> 042 * <li>Harvest name (single)</li> 043 * <li>Job start date (day of month and year)</li> 044 * <li>Job end date (day of month and year)</li> 045 * </ul> 046 * <p> 047 * The semantics of the date filters is as follows: 048 * <ol> 049 * <li>If only a start date is specified, will fetch jobs whose start date is equal or posterior</li> 050 * <li>If only an end date is specified, will fetch jobs whose end date is equal or anterior</li> 051 * <li>If both are specified, will fetch jobs whose start and end date are equal or comprised between the specified 052 * bounds.</li> 053 * </ol> 054 * <p> 055 * The class enforces that end date is set at a date posterior to start date. 056 * <p> 057 * Additionally a sort order (applied to job IDs) can be set (ascending or descending), and the query can be limited to 058 * a certain row number and a start index. 059 */ 060public class HarvestStatusQuery { 061 062 /** The String code to select all states. */ 063 public static final String JOBSTATUS_ALL = "ALL"; 064 065 /** The String code to select all harvests. */ 066 public static final String HARVEST_NAME_ALL = "ALL"; 067 /** String to check, if there is a wildcard in the harvestname. */ 068 public static final String HARVEST_NAME_WILDCARD = "*"; 069 /** Value used to define page size undefined. */ 070 public static final long PAGE_SIZE_NONE = 0; 071 /** Value used to define date undefined. */ 072 public static final long DATE_NONE = -1L; 073 074 /** 075 * Enum class defining the different sort-orders. 076 */ 077 public static enum SORT_ORDER { 078 /** Ascending mode. From lowest to highest. */ 079 ASC, 080 /** Descending mode. From highest to lowest. */ 081 DESC; 082 083 /** 084 * Parse the given argument and return a sorting order. 085 * 086 * @param order a given sorting order as string 087 * @return a sorting order representing the given string. 088 */ 089 public static SORT_ORDER parse(String order) { 090 for (SORT_ORDER o : values()) { 091 if (o.name().equals(order)) { 092 return o; 093 } 094 } 095 return null; 096 } 097 } 098 099 /** 100 * Defines the UI fields and their default values. 101 */ 102 public static enum UI_FIELD { 103 /** jobstatus with default status STARTED. */ 104 JOB_STATUS(JobStatus.STARTED.name()), 105 /** JOB ID order. default is ascending. */ 106 JOB_ID_ORDER("ASC"), 107 /** harvest name. default is ALL (i.e all harvests). */ 108 HARVEST_NAME(HARVEST_NAME_ALL), 109 /** The harvest ID. No default. */ 110 HARVEST_ID(""), 111 /** The harvest Run number. No default. */ 112 HARVEST_RUN(""), 113 /** The harvest start date. No default. */ 114 START_DATE(""), 115 /** The harvest end date. No default. */ 116 END_DATE(""), 117 /** 118 * The number of results on each page. The default is read from the setting 119 * {@link CommonSettings#HARVEST_STATUS_DFT_PAGE_SIZE}. 120 */ 121 PAGE_SIZE(Settings.get(CommonSettings.HARVEST_STATUS_DFT_PAGE_SIZE)), 122 /** The starting page. Default is 1. */ 123 START_PAGE_INDEX("1"), 124 /** The number of Jobs to resubmit identified by ID. No default. */ 125 RESUBMIT_JOB_IDS(""); 126 127 /** The default value for this UI-field. */ 128 private final String defaultValue; 129 130 /** 131 * Constructor for the UI_FIELD enum class. 132 * 133 * @param defaultValue the default value of the field. 134 */ 135 UI_FIELD(String defaultValue) { 136 this.defaultValue = defaultValue; 137 } 138 139 /** 140 * Get the values stored in the request for this UI_FIELD. 141 * 142 * @param req the servlet request 143 * @return the values stored in the request for this UI_FIELD as a string array. 144 */ 145 public String[] getValues(ServletRequest req) { 146 String[] values = req.getParameterValues(name()); 147 if (values == null || values.length == 0) { 148 return new String[] {this.defaultValue}; 149 } 150 return values; 151 } 152 153 /** 154 * Extracts the field's value from a servlet request. If the request does not define the paraeter's value, it is 155 * set to the default value. 156 * 157 * @param req a servlet request 158 * @return the field's value 159 */ 160 public String getValue(ServletRequest req) { 161 String value = req.getParameter(name()); 162 if (value == null || value.isEmpty()) { 163 return this.defaultValue; 164 } 165 return value; 166 } 167 } 168 169 /** 170 * The date format used by the calendar widget. It is actually the same format as the one represented by the 171 * DATE_FORMAT. 172 */ 173 public static final String CALENDAR_UI_DATE_FORMAT = "%Y/%m/%d"; 174 175 /** The date format used when returning dates as strings. */ 176 private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy/MM/dd"); 177 178 /** The job states selected in this query. */ 179 private Set<JobStatus> jobStatuses = new HashSet<JobStatus>(); 180 /** The harvestID. */ 181 private Long harvestId; 182 /** The harvest run number. */ 183 private Long harvestRunNumber; 184 /** The harvest name. */ 185 private String harvestName; 186 /** The start date. */ 187 private Date startDate; 188 /** The end date. */ 189 private Date endDate; 190 /** The sort order. */ 191 private SORT_ORDER sortingOrder; 192 /** The page-size. */ 193 private long pageSize; 194 /** The start page. */ 195 private long startPageIndex; 196 /** Is the harvest name case sensitive. The default is yes. */ 197 private boolean caseSensitiveHarvestName = true; 198 199 /** 200 * Builds a default query that will select all jobs. 201 */ 202 public HarvestStatusQuery() { 203 204 } 205 206 /** 207 * Builds a default query that will find jobs for a given run of a harvest. 208 * 209 * @param harvestId A given harvestId 210 * @param harvestRunNumber a given harvestRunNumber 211 */ 212 public HarvestStatusQuery(long harvestId, long harvestRunNumber) { 213 this.harvestId = harvestId; 214 this.harvestRunNumber = harvestRunNumber; 215 } 216 217 /** 218 * Builds a query from a servlet request. Unspecified fields are set to their default value. 219 * 220 * @param req a servlet request 221 */ 222 public HarvestStatusQuery(ServletRequest req) { 223 224 String[] statuses = UI_FIELD.JOB_STATUS.getValues(req); 225 for (String s : statuses) { 226 if (JOBSTATUS_ALL.equals(s)) { 227 this.jobStatuses.clear(); 228 break; 229 } 230 this.jobStatuses.add(JobStatus.parse(s)); 231 } 232 233 this.harvestName = UI_FIELD.HARVEST_NAME.getValue(req); 234 if (HARVEST_NAME_ALL.equalsIgnoreCase(this.harvestName)) { 235 this.harvestName = ""; 236 } 237 238 String harvestIdStr = UI_FIELD.HARVEST_ID.getValue(req); 239 try { 240 this.harvestId = Long.parseLong(harvestIdStr); 241 } catch (NumberFormatException e) { 242 this.harvestId = null; 243 } 244 245 String harvestRunStr = UI_FIELD.HARVEST_RUN.getValue(req); 246 try { 247 this.harvestRunNumber = Long.parseLong(harvestRunStr); 248 } catch (NumberFormatException e) { 249 this.harvestRunNumber = null; 250 } 251 252 String startDateStr = UI_FIELD.START_DATE.getValue(req); 253 if (!startDateStr.isEmpty()) { 254 try { 255 this.startDate = DATE_FORMAT.parse(startDateStr); 256 } catch (ParseException e) { 257 // Should never happen, date comes from a date selector UI 258 throw new ArgumentNotValid("Invalid date specification", e); 259 } 260 } 261 262 String endDateStr = UI_FIELD.END_DATE.getValue(req); 263 if (!endDateStr.isEmpty()) { 264 try { 265 this.endDate = DATE_FORMAT.parse(endDateStr); 266 } catch (ParseException e) { 267 // Should never happen, date comes from a date selector UI 268 throw new ArgumentNotValid("Invalid date specification", e); 269 } 270 } 271 272 if (startDate != null && endDate != null) { 273 if (endDate.before(this.startDate)) { 274 throw new ArgumentNotValid("End date is set after start date!"); 275 } 276 } 277 278 String orderStr = UI_FIELD.JOB_ID_ORDER.getValue(req); 279 this.sortingOrder = SORT_ORDER.parse(orderStr); 280 if (this.sortingOrder == null) { 281 // Will not happen as value comes from a select 282 throw new ArgumentNotValid("Invalid sort order!"); 283 } 284 285 String pageSizeStr = UI_FIELD.PAGE_SIZE.getValue(req); 286 try { 287 this.pageSize = Long.parseLong(pageSizeStr); 288 } catch (NumberFormatException e) { 289 throw new ArgumentNotValid("Invalid number!", e); 290 } 291 292 String startPageIndexStr = UI_FIELD.START_PAGE_INDEX.getValue(req); 293 try { 294 this.startPageIndex = Long.parseLong(startPageIndexStr); 295 } catch (NumberFormatException e) { 296 throw new ArgumentNotValid("Invalid number!", e); 297 } 298 299 } 300 301 /** 302 * @return the selected job states as an array. 303 */ 304 public JobStatus[] getSelectedJobStatuses() { 305 return jobStatuses.toArray(new JobStatus[jobStatuses.size()]); 306 } 307 308 /** 309 * @return the selected job states as a set.. 310 */ 311 public Set<JobStatus> getSelectedJobStatusesAsSet() { 312 return jobStatuses; 313 } 314 315 /** 316 * @return the harvest name. 317 */ 318 public String getHarvestName() { 319 if (harvestName == null) { 320 return ""; 321 } 322 return harvestName; 323 } 324 325 /** 326 * Set the harvest name. 327 * 328 * @param harvestName The harvest name 329 */ 330 public void setHarvestName(String harvestName) { 331 this.harvestName = harvestName; 332 } 333 334 /** 335 * @return the harvest ID. 336 */ 337 public Long getHarvestId() { 338 return harvestId; 339 } 340 341 /** 342 * @return the harvest run number. 343 */ 344 public Long getHarvestRunNumber() { 345 return harvestRunNumber; 346 } 347 348 /** 349 * @return the start date as milliseconds since Epoch or {@link HarvestStatusQuery#DATE_NONE} if start date is 350 * undefined 351 */ 352 public long getStartDate() { 353 return (startDate == null ? DATE_NONE : startDate.getTime()); 354 } 355 356 /** 357 * @return the end date as milliseconds since Epoch, or {@link HarvestStatusQuery#DATE_NONE} if end date is 358 * undefined 359 */ 360 public long getEndDate() { 361 return (endDate == null ? DATE_NONE : endDate.getTime()); 362 } 363 364 /** 365 * @return the start date as a string, or an empty string if start date is undefined 366 */ 367 public String getStartDateAsString() { 368 if (startDate == null) { 369 return ""; 370 } 371 return DATE_FORMAT.format(startDate); 372 } 373 374 /** 375 * @return the end date as a string, or an empty string if end date is undefined 376 */ 377 public String getEndDateAsString() { 378 if (endDate == null) { 379 return ""; 380 } 381 return DATE_FORMAT.format(endDate); 382 } 383 384 /** 385 * @return true, if the sorting order is Ascending, otherwise false. 386 */ 387 public boolean isSortAscending() { 388 return SORT_ORDER.ASC.equals(sortingOrder); 389 } 390 391 /** 392 * @return the page size, i.e. the number of results on each page. 393 */ 394 public long getPageSize() { 395 return pageSize; 396 } 397 398 /** 399 * Sets the page size. 400 * 401 * @param pageSize a number > 0. 402 */ 403 public void setPageSize(long pageSize) { 404 ArgumentNotValid.checkNotNegative(pageSize, "pageSize"); 405 this.pageSize = pageSize; 406 } 407 408 /** 409 * @return the start page 410 */ 411 public long getStartPageIndex() { 412 return startPageIndex; 413 } 414 415 /** 416 * Define whether or not the harvest name is case sensitive. 417 * 418 * @param isHarvestNameCaseSensitive If true, harvestname is case sensitive, otherwise not. 419 */ 420 public void setCaseSensitiveHarvestName(boolean isHarvestNameCaseSensitive) { 421 this.caseSensitiveHarvestName = isHarvestNameCaseSensitive; 422 } 423 424 /** 425 * @return true, if the harvest name is case sensitive, otherwise false 426 */ 427 public boolean getCaseSensitiveHarvestName() { 428 return caseSensitiveHarvestName; 429 } 430 431 /** 432 * Set the selected states in the query. 433 * 434 * @param chosenStates the set of selected states. 435 */ 436 public void setJobStatus(Set<JobStatus> chosenStates) { 437 this.jobStatuses = chosenStates; 438 } 439}