diff --git a/src/org/json/Json4Uws.java b/src/org/json/Json4Uws.java index 2549e0a0dbfbbd886758506ab99ab1c034720224..25a85c6373c8767842dda70767e014b0f1835e86 100644 --- a/src/org/json/Json4Uws.java +++ b/src/org/json/Json4Uws.java @@ -29,6 +29,7 @@ import uws.job.JobList; import uws.job.Result; import uws.job.UWSJob; import uws.job.jobInfo.JobInfo; +import uws.job.serializer.filter.JobListRefiner; import uws.job.user.JobOwner; import uws.service.UWS; import uws.service.UWSUrl; @@ -37,7 +38,7 @@ import uws.service.UWSUrl; * Useful conversion functions from UWS to JSON. * * @author Grégory Mantelet (CDS;ARI) - * @version 4.3 (09/2017) + * @version 4.3 (10/2017) */ public final class Json4Uws { @@ -75,25 +76,65 @@ public final class Json4Uws { /** * Gets the JSON representation of the given jobs list. - * @param jobsList The jobs list to represent in JSON. - * @param owner The user who asks to serialize the given jobs list. (MAY BE NULL) - * @return Its JSON representation. - * @throws JSONException If there is an error while building the JSON object. + * + * @param jobsList The jobs list to represent in JSON. + * @param owner The user who asks to serialize the given jobs list. + * (MAY BE NULL) + * + * @return Its JSON representation. + * + * @throws JSONException If there is an error while building the JSON + * object. + * + * @see #getJson(JobList, JobOwner, JobListRefiner) */ public final static JSONObject getJson(final JobList jobsList, final JobOwner owner) throws JSONException{ + return getJson(jobsList, owner, null); + } + + /** + * Gets the JSON representation of the given jobs list by filtering by owner + * and some user-filters (e.g. on phase, creation time). + * + * @param jobsList The jobs list to represent in JSON. + * @param owner The user who asks to serialize the given jobs list. + * (MAY BE NULL) + * @param listRefiner Represent all the specified job filters to apply ; + * only the job that pass through this filter should be + * displayed. If NULL, all jobs are displayed. + * + * @return Its JSON representation. + * + * @throws JSONException If there is an error while building the JSON + * object. + * + * @since 4.3 + */ + public final static JSONObject getJson(final JobList jobsList, final JobOwner owner, final JobListRefiner listRefiner) throws JSONException{ JSONObject json = new JSONObject(); if (jobsList != null){ json.put("name", jobsList.getName()); json.put("version", UWS.VERSION); JSONArray jsonJobs = new JSONArray(); UWSUrl jobsListUrl = jobsList.getUrl(); + + // Security filter: retrieve only the jobs of the specified owner: Iterator<UWSJob> it = jobsList.getJobs(owner); + + /* User filter: filter the jobs in function of filters specified by + * the user: */ + if (listRefiner != null) + it = listRefiner.refine(it); + + // Append the JSON serialization of all filtered jobs: JSONObject jsonObj = null; while(it.hasNext()){ jsonObj = getJson(it.next(), jobsListUrl, true); if (jsonObj != null) jsonJobs.put(jsonObj); + } + json.put("jobs", jsonJobs); } return json; diff --git a/src/uws/job/JobList.java b/src/uws/job/JobList.java index 3923c4a214940a6151e823310d5db3008de796e3..a19c135ad63020ee6bcda2bfe0bc9ac63d76b7fa 100644 --- a/src/uws/job/JobList.java +++ b/src/uws/job/JobList.java @@ -20,6 +20,7 @@ package uws.job; * Astronomisches Rechen Institut (ARI) */ +import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; @@ -27,6 +28,8 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import javax.servlet.ServletOutputStream; + import uws.UWSException; import uws.UWSExceptionFactory; import uws.UWSToolBox; @@ -35,6 +38,7 @@ import uws.job.manager.DefaultExecutionManager; import uws.job.manager.DestructionManager; import uws.job.manager.ExecutionManager; import uws.job.serializer.UWSSerializer; +import uws.job.serializer.filter.JobListRefiner; import uws.job.user.JobOwner; import uws.service.UWS; import uws.service.UWSService; @@ -171,7 +175,7 @@ import uws.service.log.UWSLog.LogLevel; * </i></p> * * @author Grégory Mantelet (CDS;ARI) - * @version 4.3 (09/2017) + * @version 4.3 (10/2017) * * @see UWSJob */ @@ -1000,6 +1004,45 @@ public class JobList extends SerializableUWSObject implements Iterable<UWSJob> { } } + /** + * Serializes the while object in the given output stream, + * considering the given owner ID, the given job filters and thanks to the + * given serializer. + * + * @param output The ouput stream in which this object must be + * serialized. + * @param serializer The serializer to use. + * @param ownerId The ID of the current ID. + * @param listRefiner Special filter able to refine the list of jobs with + * job filters specified by the user + * (i.e. filter, sort and limit). + * + * @throws UWSException If the owner is not allowed to see the content + * of the serializable object. + * @throws IOException If there is an error while writing in the given + * stream. + * @throws Exception If there is any other error during the + * serialization. + * + * @see UWSSerializer#getJobList(JobList, JobOwner, JobListRefiner, boolean) + * + * @since 4.3 + */ + public void serialize(ServletOutputStream output, UWSSerializer serializer, JobOwner owner, JobListRefiner listRefiner) throws UWSException, IOException, Exception{ + if (output == null) + throw new NullPointerException("Missing serialization output stream!"); + + if (owner != null && !owner.hasReadPermission(this)) + throw new UWSException(UWSException.PERMISSION_DENIED, UWSExceptionFactory.writePermissionDenied(owner, true, getName())); + + String serialization = serializer.getJobList(this, owner, listRefiner, true); + if (serialization != null){ + output.print(serialization); + output.flush(); + }else + throw new UWSException(UWSException.INTERNAL_SERVER_ERROR, "Incorrect serialization value (=NULL) ! => impossible to serialize " + toString() + "."); + } + /* ***************** */ /* INHERITED METHODS */ /* ***************** */ diff --git a/src/uws/job/serializer/JSONSerializer.java b/src/uws/job/serializer/JSONSerializer.java index 7a5089ac7c74f0b81799bf9e54636c936d362b4a..fbc30e9498f2af38d1b43ff063e1c02176cf1033 100644 --- a/src/uws/job/serializer/JSONSerializer.java +++ b/src/uws/job/serializer/JSONSerializer.java @@ -28,6 +28,7 @@ import uws.job.ErrorSummary; import uws.job.JobList; import uws.job.Result; import uws.job.UWSJob; +import uws.job.serializer.filter.JobListRefiner; import uws.job.user.JobOwner; import uws.service.UWS; import uws.service.UWSUrl; @@ -36,7 +37,7 @@ import uws.service.UWSUrl; * Lets serializing any UWS resource in JSON. * * @author Grégory Mantelet (CDS;ARI) - * @version 4.3 (09/2017) + * @version 4.3 (10/2017) * * @see Json4Uws */ @@ -54,8 +55,8 @@ public class JSONSerializer extends UWSSerializer { } @Override - public String getJobList(final JobList jobsList, final JobOwner owner, final boolean root) throws JSONException{ - return Json4Uws.getJson(jobsList, owner).toString(); + public String getJobList(final JobList jobsList, final JobOwner owner, final JobListRefiner listRefiner, final boolean root) throws JSONException{ + return Json4Uws.getJson(jobsList, owner, listRefiner).toString(); } @Override diff --git a/src/uws/job/serializer/UWSSerializer.java b/src/uws/job/serializer/UWSSerializer.java index 64f0a7307105c7b89e7c446c5c7e44608d949772..f7cf1a8af2f0cf42ef83ed260d94fb96e255dddb 100644 --- a/src/uws/job/serializer/UWSSerializer.java +++ b/src/uws/job/serializer/UWSSerializer.java @@ -28,6 +28,7 @@ import uws.job.ErrorSummary; import uws.job.JobList; import uws.job.Result; import uws.job.UWSJob; +import uws.job.serializer.filter.JobListRefiner; import uws.job.user.JobOwner; import uws.service.UWS; import uws.service.UWSUrl; @@ -41,7 +42,7 @@ import uws.service.UWSUrl; * </ul> * * @author Grégory Mantelet (CDS;ARI) - * @version 4.3 (09/2017) + * @version 4.3 (10/2017) * * @see XMLSerializer * @see JSONSerializer @@ -207,23 +208,58 @@ public abstract class UWSSerializer implements Serializable { * @return The serialization of the given jobs list. * * @throws Exception If there is an error during the serialization. + * + * @see #getJobList(JobList, JobOwner, boolean) */ public String getJobList(final JobList jobsList, final boolean root) throws Exception{ return getJobList(jobsList, null, root); } /** - * Serializes the given jobs list. + * Serializes the given jobs list, by filtering by user. * * @param jobsList The jobs list to serialize. - * @param owner The user which has asked the serialization of the given jobs list. - * @param root <i>false</i> if the jobs list to serialize will be included - * in a top level serialization (for a jobs list: uws), <i>true</i> otherwise. + * @param owner The user which has asked the serialization of the + * given jobs list. If NULL, all anonymous jobs are + * displayed. + * @param root <code>false</code> if the jobs list to serialize + * will be included in a top level serialization (for a + * jobs list: uws), + * <code>true</code> otherwise. + * + * @return The serialization of the given jobs list. + * + * @throws Exception If there is an error during the serialization. + * + * @see #getJobList(JobList, JobOwner, JobListRefiner, boolean) + */ + public String getJobList(final JobList jobsList, JobOwner owner, final boolean root) throws Exception{ + return getJobList(jobsList, null, null, root); + } + + /** + * Serializes the given jobs list, by filtering using user-specified + * filters. + * + * @param jobsList The jobs list to serialize. + * @param owner The user which has asked the serialization of the + * given jobs list. If NULL, all anonymous jobs are + * displayed. + * @param phaseFilters Represent all the specified job filters to apply ; + * only the job that pass through this filter should be + * displayed. If NULL, all jobs are displayed. + * @param root <code>false</code> if the jobs list to serialize + * will be included in a top level serialization (for a + * jobs list: uws), + * <code>true</code> otherwise. + * * @return The serialization of the given jobs list. * * @throws Exception If there is an error during the serialization. + * + * @since 4.3 */ - public abstract String getJobList(final JobList jobsList, JobOwner owner, final boolean root) throws Exception; + public abstract String getJobList(final JobList jobsList, JobOwner owner, final JobListRefiner listRefiner, final boolean root) throws Exception; /** * Serializes the whole given job. diff --git a/src/uws/job/serializer/XMLSerializer.java b/src/uws/job/serializer/XMLSerializer.java index 44cb254e1ffed0700d271f7dc1eb7dfce9b47964..b44bdf317f8d637a66dc429d2f2b89b7fd034437 100644 --- a/src/uws/job/serializer/XMLSerializer.java +++ b/src/uws/job/serializer/XMLSerializer.java @@ -31,6 +31,7 @@ import uws.job.JobList; import uws.job.Result; import uws.job.UWSJob; import uws.job.jobInfo.JobInfo; +import uws.job.serializer.filter.JobListRefiner; import uws.job.user.JobOwner; import uws.service.UWS; import uws.service.UWSUrl; @@ -40,7 +41,7 @@ import uws.service.request.UploadFile; * Lets serializing any UWS resource in XML. * * @author Grégory Mantelet (CDS;ARI) - * @version 4.3 (09/2017) + * @version 4.3 (10/2017) */ public class XMLSerializer extends UWSSerializer { private static final long serialVersionUID = 1L; @@ -178,7 +179,7 @@ public class XMLSerializer extends UWSSerializer { } @Override - public String getJobList(final JobList jobsList, final JobOwner owner, final boolean root){ + public String getJobList(final JobList jobsList, final JobOwner owner, final JobListRefiner listRefiner, final boolean root) throws Exception{ StringBuffer xml = new StringBuffer(getHeader()); xml.append("<jobs version=\"").append(UWS.VERSION).append('"').append(getUWSNamespace(true)); @@ -190,7 +191,16 @@ public class XMLSerializer extends UWSSerializer { xml.append('>'); UWSUrl jobsListUrl = jobsList.getUrl(); + + // Security filter: retrieve only the jobs of the specified owner: Iterator<UWSJob> it = jobsList.getJobs(owner); + + /* User filter: filter the jobs in function of filters specified by the + * user: */ + if (listRefiner != null) + it = listRefiner.refine(it); + + // Append the jobs' description: while(it.hasNext()) xml.append("\n\t").append(getJobRef(it.next(), jobsListUrl)); diff --git a/src/uws/job/serializer/filter/AfterFilter.java b/src/uws/job/serializer/filter/AfterFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..bcf3094fc1528c658409d1da29fe793dfbf72c9f --- /dev/null +++ b/src/uws/job/serializer/filter/AfterFilter.java @@ -0,0 +1,71 @@ +package uws.job.serializer.filter; + +/* + * This file is part of UWSLibrary. + * + * UWSLibrary is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UWSLibrary 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UWSLibrary. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2017 - Astronomisches Rechen Institut (ARI) + */ + +import java.util.Date; + +import uws.job.UWSJob; + +/** + * Job filter based on the creation time. + * + * <p> + * Only jobs that have been created after a given date (included) are kept. + * </p> + * + * @author Grégory Mantelet (ARI) + * @version 4.3 (10/2017) + * @since 4.3 + */ +public final class AfterFilter implements JobFilter { + + /** The date after which jobs must be kept. */ + private final Date limit; + + /** + * Build the {@link AfterFilter} with the given limit date. + * + * @param date The date (non-included) after which jobs must be kept. + * + * @throws NullPointerException If the given date is NULL. + */ + public AfterFilter(final Date date) throws NullPointerException{ + if (date == null) + throw new NullPointerException("Missing limit date! Can not create an AfterFilter."); + + limit = date; + } + + /** + * Get the date which filters jobs on their creationTime. + * Only jobs created after this date will be retained. + * + * @return The limit date. + */ + public final Date getDate(){ + return limit; + } + + @Override + public boolean match(final UWSJob job){ + return (job != null) && (job.getCreationTime() != null) && (job.getCreationTime().after(limit)); + } + +} \ No newline at end of file diff --git a/src/uws/job/serializer/filter/JobFilter.java b/src/uws/job/serializer/filter/JobFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..ec2ef9c4628a55557ba97943064a420289091912 --- /dev/null +++ b/src/uws/job/serializer/filter/JobFilter.java @@ -0,0 +1,50 @@ +package uws.job.serializer.filter; + +/* + * This file is part of UWSLibrary. + * + * UWSLibrary is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UWSLibrary 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UWSLibrary. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2017 - Astronomisches Rechen Institut (ARI) + */ + +import uws.job.UWSJob; + +/** + * Definition of a filter aiming to reduce a list of jobs. + * + * @author Grégory Mantelet (ARI) + * @version 4.3 (10/2017) + * @since 4.3 + * + * @see JobFilters + */ +public interface JobFilter { + + /** + * Tell whether the given job match this filter. + * + * <p><i>Note: + * In case of error while evaluating this filter on the given job, + * <code>false</code> will be returned. + * </i></p> + * + * @param job A job to filter. + * + * @return <code>true</code> if the job matches this filter, + * <code>false</code> otherwise. + */ + public boolean match(final UWSJob job); + +} \ No newline at end of file diff --git a/src/uws/job/serializer/filter/JobListRefiner.java b/src/uws/job/serializer/filter/JobListRefiner.java new file mode 100644 index 0000000000000000000000000000000000000000..d36c200b1d11be8e5bada82338cb1965f3587fb4 --- /dev/null +++ b/src/uws/job/serializer/filter/JobListRefiner.java @@ -0,0 +1,473 @@ +package uws.job.serializer.filter; + +/* + * This file is part of UWSLibrary. + * + * UWSLibrary is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UWSLibrary 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UWSLibrary. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2017 - Astronomisches Rechen Institut (ARI) + */ + +import java.text.ParseException; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +import javax.servlet.http.HttpServletRequest; + +import uws.ISO8601Format; +import uws.UWSException; +import uws.job.ExecutionPhase; +import uws.job.UWSJob; + +/** + * Let filter (and optionally order) a list of jobs according to the filter + * parameters given in an {@link HttpServletRequest}. + * + * <p>Only the following HTTP-GET parameters will generate a job filter:</p> + * <ul> + * <li><b>PHASE</b>: a single legal phase is expected. Only jobs having this + * phase will pass through this filter. + * + * <p><i><b>Note:</b>Several <code>PHASE</code> parameters + * with different execution phases may be provided. Their + * effect will be joint with a logical OR (so, all jobs of + * one of the given phases will pass the filter).</i></p></li> + * + * <li><b>AFTER</b>: an ISO-8601 date is expected. Only jobs started + * after this date will pass through the filter. + * + * <p><i><b>Note:</b> If several <code>AFTER</code> + * parameters are provided, only the one with the most recent + * date will be taken into account. For instance: + * <code>?AFTER=2015-01-01T12:00:00Z&AFTER=2014-01-01T12:00:00Z</code> + * will be interpreted as just 1 AFTER filter with the date + * <code>2015-01-01T12:00:00Z</code>.</i></p></li> + * + * <li><b>LAST</b>: a positive integer number is expected. Only the LAST most + * recently created jobs will pass through the filter. + * The jobs will be sorted by descending creationTime. + * + * <p><i><b>Note:</b> If several <code>LAST</code> + * parameters are provided, only the smallest positive (and + * not null) value will be taken into account.</i></p> + * </ul> + * + * <p><i><b>IMPORTANT Note:</b> + * If no PHASE filter is specified, a default one rejecting ARCHIVED filter is + * set by default. This is specified in UWS 1.1 standard so that being backward + * compatible with the version 1.0 of the standard in which no ARCHIVED phase + * existed. + * </i></p> + * + * @author Grégory Mantelet (ARI) + * @version 4.3 (10/2017) + * @since 4.3 + */ +public class JobListRefiner { + + /** List of all job filters to apply. All of these filters must match in + * order to keep a job. */ + protected final List<JobFilter> filters = new ArrayList<JobFilter>(); + + /** List of retained jobs after filtering. */ + protected List<UWSJob> jobList = new ArrayList<UWSJob>(); + + /** Indicate how jobs must be sorted in the {@link #jobList} list. + * + * <p><i>Note: + * If NULL, jobs are merely added at the end of the list: no order. + * </i></p> */ + protected Comparator<UWSJob> sortComp = null; + + /** Indicate how many of the first jobs of the list {@link #jobList} must be + * really retained. + * + * <p><i>Note: + * If negative or null, all jobs are of the filtered list are returned. + * </i></p> */ + protected int topSize = -1; + + /** Indicate if the jobs of the {@link #jobList} list must be returned from + * the beginning to the end (<code>false</code>) or from the end to the + * beginning (<code>true</code>). + * + * <p><i>Note: + * If {@link #topSize} have a positive and not null value, this attribute + * will then apply on the {@link #topSize} first jobs: if <code>false</code> + * jobs from 0 to {@link #topSize} are returned, otherwise they are + * returned from {@link #topSize} to 0. + * </i></p> */ + protected boolean reverseOrder = false; + + /** + * Empty constructor. No filter and no sorting is done here. + * All jobs given to the {@link #filter(Iterator)} function will then be + * allowed. + */ + protected JobListRefiner(){} + + /** + * Build a filter for a whole list of jobs. + * + * <p> + * This filter will actually be composed of several Job filters, + * in function of the HTTP-GET parameters specified in the given HTTP + * request. + * </p> + * + * <p>Only the following HTTP-GET parameters will generate a job filter:</p> + * <ul> + * <li><b>PHASE</b>: a single legal phase is expected. Only jobs having + * this phase will pass through this filter. + * + * <p><i><b>Note:</b>Several <code>PHASE</code> + * parameters with different execution phases may be + * provided. Their effect will be joint with a logical OR + * (so, all jobs of one of the given phases will pass the + * filter).</i></p></li> + * + * <li><b>AFTER</b>: an ISO-8601 date is expected. Only jobs started + * after this date will pass through the filter. + * + * <p><i><b>Note:</b> If several <code>AFTER</code> + * parameters are provided, only the one with the most + * recent date will be taken into account. For instance: + * <code>?AFTER=2015-01-01T12:00:00Z&AFTER=2014-01-01T12:00:00Z</code> + * will be interpreted as just 1 AFTER filter with the + * date <code>2015-01-01T12:00:00Z</code>.</i></p></li> + * + * <li><b>LAST</b>: a positive integer number is expected. Only the LAST + * most recently created jobs will pass through the + * filter. The jobs will be sorted by descending + * creationTime. + * + * <p><i><b>Note:</b> If several <code>LAST</code> + * parameters are provided, only the smallest positive + * (and not null) value will be taken into account.</i></p> + * </ul> + * + * <p><i><b>IMPORTANT Note:</b> + * If no PHASE filter is specified, a default one rejecting ARCHIVED filter + * is set by default. This is specified in UWS 1.1 standard so that being + * backward compatible with the version 1.0 of the standard in which no + * ARCHIVED phase existed. + * </i></p> + * + * @param request An HTTP request in which HTTP-GET parameters correspond + * to Job filters to create. + * + * @throws UWSException If the value of at least one AFTER, PHASE or LAST + * parameter is incorrect. + */ + public JobListRefiner(final HttpServletRequest request) throws UWSException{ + String pName; + String[] values; + + ExecutionPhase phase = null; + Date afterDate = null; + int last = -1; + + AfterFilter afterFilter = null; + PhasesFilter phasesFilter = null; + + /* *************************************************** */ + /* Identify all filters inside the HTTP-GET parameters */ + + Enumeration<String> paramNames = request.getParameterNames(); + while(paramNames.hasMoreElements()){ + pName = paramNames.nextElement(); + values = request.getParameterValues(pName); + // Case: PHASE (case INsensitively): + if (pName.toUpperCase().equals("PHASE")){ + for(String p : values){ + if (p != null){ + try{ + // resolve the execution phase: + phase = ExecutionPhase.valueOf(p.toUpperCase()); + // add the phase into the PhasesFilter: + if (phasesFilter == null) + phasesFilter = new PhasesFilter(phase); + else + phasesFilter.add(phase); + }catch(IllegalArgumentException iae){ + throw new UWSException(UWSException.BAD_REQUEST, "Incorrect PHASE value: \"" + p + "\"! No such execution phase is known by this service."); + } + } + } + }else if (pName.toUpperCase().equals("AFTER")){ + for(String p : values){ + if (p != null){ + try{ + // resolve date: + afterDate = ISO8601Format.parseToDate(p); + /* create/replace the AfterFilter if the date is + * more recent: */ + if (afterFilter == null || afterDate.after(afterFilter.getDate())) + afterFilter = new AfterFilter(afterDate); + }catch(ParseException pe){ + throw new UWSException(UWSException.BAD_REQUEST, "Incorrect AFTER value: \"" + p + "\"! The date must be formatted in ISO-8601."); + } + } + } + }else if (pName.toUpperCase().equals("LAST")){ + for(String p : values){ + if (p != null){ + try{ + // resolve the number of jobs to fetch: + last = Integer.parseInt(p); + /* update the last counter (the value is updated + * only if the new value is positive and smaller): */ + if (last >= 0 && (topSize < 0 || last < topSize)) + topSize = last; + else if (last < 0) + throw new UWSException(UWSException.BAD_REQUEST, "Incorrect LAST value: \"" + p + "\"! A positive integer was expected."); + }catch(NumberFormatException nfe){ + throw new UWSException(UWSException.BAD_REQUEST, "Incorrect LAST value: \"" + p + "\"! A positive integer was expected."); + } + } + } + } + } + + /* ************************ */ + /* Append all found filters */ + + /* Set the PHASES filter (if no filter is specified, a default one + * forbidding ARCHIVED jobs is set): */ + if (phasesFilter != null) + filters.add(phasesFilter); + else + filters.add(new NoArchivedFilter()); + + // Set the AFTER filter: + if (afterFilter != null) + filters.add(afterFilter); + + // Set the LAST filter: + if (topSize >= 0){ + /* jobs are sorted by descending creation-time (so that only the + * topSize first can be easily read): */ + sortComp = new JobComparator(); + /* the topSize most recently jobs must be returned in descending creation-time, + * so the order of jobs set by the sortComp comparator must NOT be reversed: */ + reverseOrder = false; + } + + } + + /** + * Add the given job in the temporary internal list of filtered jobs by + * preserving the specified sorting. + * + * <p> + * This function added the job at the end of the list IF no sort is + * required. Otherwise, a binary search is performed and the job is added + * at the right place in the list so that keeping the list sorted. + * </p> + * + * @param job The job to keep in the display-able job list. + */ + protected final void addJob(final UWSJob job){ + if (job == null) + return; + + if (sortComp == null) + jobList.add(job); + else{ + int index = Collections.binarySearch(jobList, job, sortComp); + if (index < 0) + index = -(index + 1); + jobList.add(index, job); + } + } + + /** + * Filter (and eventually sort and/or limit in size) the given list of jobs. + * + * @param jobList Job list to filter. + * + * @return The filtered (and eventually sorted/limited) job list. + */ + @SuppressWarnings("rawtypes") + public Iterator<UWSJob> refine(final Iterator<UWSJob> jobList){ + // Remove all items of the last filtering result: + if (this.jobList instanceof AbstractList) + ((AbstractList)this.jobList).clear(); + else{ + while(!this.jobList.isEmpty()) + this.jobList.remove(0); + } + + // Filters the given jobs with the simple job filters: + UWSJob job; + while(jobList.hasNext()){ + job = jobList.next(); + + // Apply the job filters on this job and retain it if it passes them: + if (match(job)) + addJob(job); // if a sort must be done, it is performed here by #addJob(UWSJob) + } + + // Return an iterator on this whole filtered job list: + if (topSize < 0) + return this.jobList.iterator(); + + // OR Return an iterator on the topSize first jobs (in the current order or reverse): + else + return new TopIterator(this.jobList, topSize, reverseOrder); + } + + /** + * Tell whether the given job matches all the job filters. + * + * <p> + * In other words, this function operates a logical AND between all listed + * filters. + * </p> + * + * <p><i>Note: + * If the given job is NULL, <code>false</code> will be returned. + * In case of error while evaluating one of the filters on the given job, + * <code>false</code> will be returned as well. + * </i></p> + * + * @param job A job to filter. + * + * @return <code>true</code> if the job matches all the filters, + * <code>false</code> otherwise. + */ + protected final boolean match(final UWSJob job){ + if (job == null) + return false; + + for(JobFilter filter : filters){ + if (!filter.match(job)) + return false; + } + + return true; + } + + /** + * Compare the 2 given {@link UWSJob} instances by using only their creation + * date/time. The most recently created job is considered as inferior. So, + * this comparator aims to sort jobs by descending creation date/time. + * + * <p><i><b>WARNING!</b> + * It must be ensured that all compared jobs have always a NOT-NULL + * creationTime attribute. Otherwise this comparator may fail or return an + * incorrect value. + * </i></p> + * + * @author Grégory Mantelet (ARI) + * @version 4.3 (10/2017) + * @since 4.3 + */ + public final static class JobComparator implements Comparator<UWSJob> { + @Override + public int compare(UWSJob o1, UWSJob o2){ + return -(o1.getCreationTime().compareTo(o2.getCreationTime())); + } + } + + /** + * This iterator is designed to return just the N first items of the given + * list. + * + * <p> + * It is also possible to inverse the order of these N first items by setting + * the last constructor parameter to <code>true</code>. + * </p> + * + * <p><i>Note: + * This iterator does not support the remove operation ; + * the function {@link #remove()} will then return an + * {@link UnsupportedOperationException}. + * </i></p> + * + * @author Grégory Mantelet (ARI) + * @version 4.3 (10/2017) + * @since 4.3 + */ + protected final static class TopIterator implements Iterator<UWSJob> { + + /** Jobs list on which this iterator must iterate. */ + private final List<UWSJob> list; + /** The number of items that must be read by this iterator. */ + private final int topSize; + /** Indicate whether the topSize items must be read in the given order + * or in the reverse one. */ + private final boolean reverseOrder; + + /** Index of the last read item. */ + private int currentIndex = -1; + /** Number of read items. */ + private int count = 0; + + /** + * Create an iterator which will read the <code>topSize</code> first + * item of the given list. + * + * @param joblist List of jobs to return. + * @param topSize Number of items to read from the beginning of the + * list. + * @param reverse <code>true</code> if the <code>topSize</code> first + * items must be read in the given order, + * <code>false</code> if they must be read in the + * reverse order. + */ + public TopIterator(final List<UWSJob> joblist, final int topSize, final boolean reverse){ + this.list = joblist; + this.topSize = topSize; + this.reverseOrder = reverse; + + if (reverseOrder && topSize >= 0) + currentIndex = (list.size() <= topSize) ? list.size() : topSize; + else + currentIndex = reverseOrder ? list.size() : -1; + + count = 0; + } + + @Override + public boolean hasNext(){ + return (topSize < 0 || count + 1 <= topSize) && (reverseOrder ? currentIndex - 1 >= 0 : currentIndex + 1 < list.size()); + } + + @Override + public UWSJob next(){ + if (!hasNext()) + throw new NoSuchElementException("No more jobs in this filtered job list!"); + + count++; + currentIndex = reverseOrder ? currentIndex - 1 : currentIndex + 1; + + return list.get(currentIndex); + } + + @Override + public void remove(){ + throw new UnsupportedOperationException("No remove operation possible on this iterator of filtered job list!"); + } + + } + +} \ No newline at end of file diff --git a/src/uws/job/serializer/filter/NoArchivedFilter.java b/src/uws/job/serializer/filter/NoArchivedFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..688f9e13181f521b43ce61f98be17dd4ad872159 --- /dev/null +++ b/src/uws/job/serializer/filter/NoArchivedFilter.java @@ -0,0 +1,39 @@ +package uws.job.serializer.filter; + +/* + * This file is part of UWSLibrary. + * + * UWSLibrary is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UWSLibrary 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UWSLibrary. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2017 - Astronomisches Rechen Institut (ARI) + */ + +import uws.job.ExecutionPhase; +import uws.job.UWSJob; + +/** + * Job filter which allows all jobs except those in ARCHIVED phase. + * + * @author Grégory Mantelet (ARI) + * @version 4.3 (10/2017) + * @since 4.3 + */ +public class NoArchivedFilter implements JobFilter { + + @Override + public boolean match(final UWSJob job){ + return (job != null) && (job.getPhase() != ExecutionPhase.ARCHIVED); + } + +} \ No newline at end of file diff --git a/src/uws/job/serializer/filter/PhasesFilter.java b/src/uws/job/serializer/filter/PhasesFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..dd2aa76beda7c0e43bb3a1d65ac5c4a4da8cf6f6 --- /dev/null +++ b/src/uws/job/serializer/filter/PhasesFilter.java @@ -0,0 +1,99 @@ +package uws.job.serializer.filter; + +/* + * This file is part of UWSLibrary. + * + * UWSLibrary is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * UWSLibrary 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with UWSLibrary. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2017 - Astronomisches Rechen Institut (ARI) + */ + +import java.util.ArrayList; +import java.util.List; + +import uws.job.ExecutionPhase; +import uws.job.UWSJob; + +/** + * Job filter based on the execution phase. + * + * <p> + * Only jobs that are in one of the execution phases listed in this filter are + * kept. + * </p> + * + * <p> + * The constructor of this filter requires exactly one execution phase. + * But obviously more phases can be added to the list of accepted execution + * phases by using the function {@link #add(ExecutionPhase)}. + * </p> + * + * @author Grégory Mantelet (ARI) + * @version 4.3 (10/2017) + * @since 4.3 + */ +public final class PhasesFilter implements JobFilter { + + /** List of execution phases in which jobs to keep must be. */ + protected final List<ExecutionPhase> phases = new ArrayList<ExecutionPhase>(); + + /** + * Build a {@link PhasesFilter} which will retain only jobs in the given + * execution phase. + * + * <p> + * More phases can be added into the list of accepted phases thanks to the + * function {@link #add(ExecutionPhase)}. + * </p> + * + * @param phase An execution phase on which jobs to retain must be. + * + * @throws NullPointerException If the given phase is NULL. + */ + public PhasesFilter(final ExecutionPhase phase) throws NullPointerException{ + if (phase == null) + throw new NullPointerException("Missing execution phase! Can not ceate a PhasesFilter without at least one execution phase."); + + phases.add(phase); + } + + /** + * Add the given phase in the list of accepted phases. + * + * <p><i>Note: + * The given phase is not added into the list if it is already inside. + * </i></p> + * + * @param phase An execution phase to add inside the list of accepted + * phases. + */ + public void add(final ExecutionPhase phase){ + if (phase != null && !phases.contains(phase)) + phases.add(phase); + } + + @Override + public boolean match(final UWSJob job){ + if (job == null) + return false; + + for(ExecutionPhase p : phases){ + if (job.getPhase() == p) + return true; + } + + return false; + } + +} \ No newline at end of file diff --git a/src/uws/service/UWSServlet.java b/src/uws/service/UWSServlet.java index 60355f690ec44d2ba0a6aadd64fee835ac2d83b7..e2015fb55964658945e09c3be2e7f67bfd32edbf 100644 --- a/src/uws/service/UWSServlet.java +++ b/src/uws/service/UWSServlet.java @@ -57,6 +57,7 @@ import uws.job.parameters.UWSParameters; import uws.job.serializer.JSONSerializer; import uws.job.serializer.UWSSerializer; import uws.job.serializer.XMLSerializer; +import uws.job.serializer.filter.JobListRefiner; import uws.job.user.JobOwner; import uws.service.actions.UWSAction; import uws.service.backup.UWSBackupManager; @@ -150,7 +151,7 @@ import uws.service.request.UploadFile; * </p> * * @author Grégory Mantelet (CDS;ARI) - * @version 4.3 (09/2017) + * @version 4.3 (10/2017) */ public abstract class UWSServlet extends HttpServlet implements UWS, UWSFactory { private static final long serialVersionUID = 1L; @@ -536,7 +537,7 @@ public abstract class UWSServlet extends HttpServlet implements UWS, UWSFactory resp.setContentType(serializer.getMimeType()); resp.setCharacterEncoding(UWSToolBox.DEFAULT_CHAR_ENCODING); try{ - jobsList.serialize(resp.getOutputStream(), serializer, user); + jobsList.serialize(resp.getOutputStream(), serializer, user, new JobListRefiner(req)); }catch(Exception e){ if (!(e instanceof UWSException)){ getLogger().logUWS(LogLevel.ERROR, requestUrl, "SERIALIZE", "Can not serialize the jobs list \"" + jobsList.getName() + "\"!", e); diff --git a/src/uws/service/actions/ListJobs.java b/src/uws/service/actions/ListJobs.java index 2ce5c852cd32b2d6cf99ef66ee23d5f6ec25bbdf..57f7947ee672293ed3b6f2de6864aebca35ea3b4 100644 --- a/src/uws/service/actions/ListJobs.java +++ b/src/uws/service/actions/ListJobs.java @@ -2,21 +2,21 @@ package uws.service.actions; /* * This file is part of UWSLibrary. - * + * * UWSLibrary is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * UWSLibrary 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 Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with UWSLibrary. If not, see <http://www.gnu.org/licenses/>. - * - * Copyright 2012-2015 - UDS/Centre de DonnĂ©es astronomiques de Strasbourg (CDS), + * + * Copyright 2012-2017 - UDS/Centre de DonnĂ©es astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institut (ARI) */ @@ -30,21 +30,27 @@ import uws.UWSException; import uws.UWSToolBox; import uws.job.JobList; import uws.job.serializer.UWSSerializer; +import uws.job.serializer.filter.JobListRefiner; import uws.job.user.JobOwner; import uws.service.UWSService; import uws.service.UWSUrl; import uws.service.log.UWSLog.LogLevel; /** - * <p>The "List Jobs" action of a UWS.</p> - * - * <p><i><u>Note:</u> The corresponding name is {@link UWSAction#LIST_JOBS}.</i></p> - * - * <p>This action returns the list of jobs contained in the jobs list specified by the URL of the request. - * This list is serialized by the {@link UWSSerializer} choosed in function of the HTTP Accept header.</p> - * + * The "List Jobs" action of a UWS. + * + * <p><i>Note: + * The corresponding name is {@link UWSAction#LIST_JOBS}. + * </i></p> + * + * <p> + * This action returns the list of jobs contained in the jobs list specified by + * the URL of the request. This list is serialized by the {@link UWSSerializer} + * chosen in function of the HTTP Accept header. + * </p> + * * @author Grégory Mantelet (CDS;ARI) - * @version 4.1 (04/2015) + * @version 4.3 (10/2017) */ public class ListJobs extends UWSAction { private static final long serialVersionUID = 1L; @@ -70,11 +76,12 @@ public class ListJobs extends UWSAction { /** * Checks whether: * <ul> - * <li>a job list name is specified in the given UWS URL <i>(<u>note:</u> the existence of the jobs list is not checked)</i>,</li> + * <li>a job list name is specified in the given UWS URL + * <i>(<u>note:</u> the existence of the jobs list is not checked)</i>,</li> * <li>the UWS URL does not make a reference to a job (so: no job ID),</li> * <li>the HTTP method is HTTP-GET.</li> * </ul> - * + * * @see uws.service.actions.UWSAction#match(UWSUrl, JobOwner, HttpServletRequest) */ @Override @@ -84,12 +91,14 @@ public class ListJobs extends UWSAction { /** * Gets the specified jobs list <i>(and throw an error if not found)</i>, - * chooses the serializer and write the serialization of the jobs list in the given response. - * + * chooses the serializer and write the serialization of the jobs list in + * the given response. + * * @see #getJobsList(UWSUrl) + * @see JobListRefiner#JobListRefiner(HttpServletRequest) * @see UWSService#getSerializer(String) - * @see JobList#serialize(ServletOutputStream, UWSSerializer, JobOwner) - * + * @see JobList#serialize(ServletOutputStream, UWSSerializer, JobOwner, JobListRefiner) + * * @see uws.service.actions.UWSAction#apply(UWSUrl, JobOwner, HttpServletRequest, HttpServletResponse) */ @Override @@ -102,7 +111,7 @@ public class ListJobs extends UWSAction { response.setContentType(serializer.getMimeType()); response.setCharacterEncoding(UWSToolBox.DEFAULT_CHAR_ENCODING); try{ - jobsList.serialize(response.getOutputStream(), serializer, user); + jobsList.serialize(response.getOutputStream(), serializer, user, new JobListRefiner(request)); }catch(Exception e){ if (!(e instanceof UWSException)){ getLogger().logUWS(LogLevel.ERROR, urlInterpreter, "SERIALIZE", "Can not serialize the jobs list \"" + jobsList.getName() + "\"!", e); diff --git a/test/uws/job/serializer/filter/TestAfterFilter.java b/test/uws/job/serializer/filter/TestAfterFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..fe6cc9caefa8d447e0f273f71bb73dbce3f4c560 --- /dev/null +++ b/test/uws/job/serializer/filter/TestAfterFilter.java @@ -0,0 +1,52 @@ +package uws.job.serializer.filter; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Date; +import java.util.GregorianCalendar; + +import org.junit.Test; + +import uws.job.UWSJob; +import uws.job.parameters.UWSParameters; + +public class TestAfterFilter { + + @Test + public void testMatch(){ + GregorianCalendar cal = new GregorianCalendar(2010, 3, 1, 1, 0, 0); + + JobFilter filter = new AfterFilter(cal.getTime()); + + // 1 second after => OK! + cal.set(2010, 3, 1, 1, 0, 1); + UWSJob testJob = new UWSJob("123456", cal.getTimeInMillis(), null, new UWSParameters(), -1, -1, -1, null, null); + assertTrue(filter.match(testJob)); + + // Now => OK! + testJob = new UWSJob("123456", (new Date()).getTime(), null, new UWSParameters(), -1, (new Date()).getTime(), -1, null, null); + assertTrue(filter.match(testJob)); + + // Exactly same date => Nop! + cal.set(2010, 3, 1, 1, 0, 0); + testJob = new UWSJob("123456", cal.getTimeInMillis(), null, new UWSParameters(), -1, cal.getTimeInMillis(), -1, null, null); + assertFalse(filter.match(testJob)); + + // 1 second before => Nop! + cal.set(2010, 3, 1, 0, 59, 59); + testJob = new UWSJob("123456", cal.getTimeInMillis(), null, new UWSParameters(), -1, cal.getTimeInMillis(), -1, null, null); + assertFalse(filter.match(testJob)); + + // No start time => does not matter...we are working on the creationTime now + /* Note: this test is here just to ensure we are effectively working on the creationTime! */ + testJob = new UWSJob("123456", cal.getTimeInMillis(), null, new UWSParameters(), -1, -1, -1, null, null); + assertFalse(filter.match(testJob)); + testJob = new UWSJob("123456", (new Date()).getTime(), null, new UWSParameters(), -1, -1, -1, null, null); + assertTrue(filter.match(testJob)); + + // No job => Nop! + assertFalse(filter.match(null)); + } + +} \ No newline at end of file diff --git a/test/uws/job/serializer/filter/TestJobListRefiner.java b/test/uws/job/serializer/filter/TestJobListRefiner.java new file mode 100644 index 0000000000000000000000000000000000000000..f9bbad356ed75fb022ec969d50ebb9baa96923a9 --- /dev/null +++ b/test/uws/job/serializer/filter/TestJobListRefiner.java @@ -0,0 +1,1085 @@ +package uws.job.serializer.filter; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Date; +import java.util.Enumeration; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +import javax.servlet.AsyncContext; +import javax.servlet.DispatcherType; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.servlet.http.Part; + +import org.junit.Test; + +import uws.ISO8601Format; +import uws.UWSException; +import uws.job.ErrorSummary; +import uws.job.ErrorType; +import uws.job.ExecutionPhase; +import uws.job.UWSJob; +import uws.job.parameters.UWSParameters; +import uws.job.serializer.filter.JobListRefiner.TopIterator; + +public class TestJobListRefiner { + + @Test + public void testJobListRefiner(){ + TestHttpServletRequest request = new TestHttpServletRequest(); + JobListRefiner filter; + try{ + // NO PARAMS => Nothing set! + filter = new JobListRefiner(request); + assertEquals(1, filter.filters.size()); + assertEquals(NoArchivedFilter.class, filter.filters.get(0).getClass()); + assertEquals(-1, filter.topSize); + assertFalse(filter.reverseOrder); + assertNull(filter.sortComp); + + // A NON FILTER PARAMETER => Nothing set! + request.addParams("Nothing", "Blabla"); + filter = new JobListRefiner(request); + assertEquals(1, filter.filters.size()); + assertEquals(NoArchivedFilter.class, filter.filters.get(0).getClass()); + assertEquals(-1, filter.topSize); + assertFalse(filter.reverseOrder); + assertNull(filter.sortComp); + }catch(UWSException ue){ + ue.printStackTrace(System.err); + fail("No error should happen if no LAST, PHASE or AFTER parameter is provided."); + } + + /* ************* */ + /* FILTER: AFTER */ + + // INCORRECT AFTER PARAMETER => Nothing set and no error! + try{ + request.clearParams(); + request.addParams("AFTER", "foo"); + filter = new JobListRefiner(request); + fail("The provided parameter AFTER is NOT AT ALL an ISO-8601 date. An error should have occurred."); + }catch(UWSException ue){ + assertEquals("Incorrect AFTER value: \"foo\"! The date must be formatted in ISO-8601.", ue.getMessage()); + assertEquals(UWSException.BAD_REQUEST, ue.getHttpErrorCode()); + } + + try{ + // CORRECT AFTER PARAMETER => an AfterFilter should be set! + // With just a full date: + request.clearParams(); + request.addParams("AFTER", "2015-01-01"); + filter = new JobListRefiner(request); + assertEquals(2, filter.filters.size()); + assertEquals(NoArchivedFilter.class, filter.filters.get(0).getClass()); + assertEquals(AfterFilter.class, filter.filters.get(1).getClass()); + assertEquals("2015-01-01T00:00:00Z", ISO8601Format.format(((AfterFilter)filter.filters.get(1)).getDate())); + assertEquals(-1, filter.topSize); + assertFalse(filter.reverseOrder); + assertNull(filter.sortComp); + request.clearParams(); + // With a full date and time: + request.addParams("AFTER", "2015-01-01T12:00:00"); + filter = new JobListRefiner(request); + assertEquals(2, filter.filters.size()); + assertEquals(NoArchivedFilter.class, filter.filters.get(0).getClass()); + assertEquals(AfterFilter.class, filter.filters.get(1).getClass()); + assertEquals("2015-01-01T12:00:00Z", ISO8601Format.format(((AfterFilter)filter.filters.get(1)).getDate())); + assertEquals(-1, filter.topSize); + assertFalse(filter.reverseOrder); + assertNull(filter.sortComp); + + // CORRECT 3 AFTER PARAMETERS => a single AfterFilter with the most recent date should be set! + request.clearParams(); + request.addParams("AFTER", "2014-01-01T12:00:00"); + request.addParams("after", "2015-01-30T12:00:00"); + request.addParams("AFTER", "2015-01-01T12:00:00"); + filter = new JobListRefiner(request); + assertEquals(2, filter.filters.size()); + assertEquals(NoArchivedFilter.class, filter.filters.get(0).getClass()); + assertEquals(AfterFilter.class, filter.filters.get(1).getClass()); + assertEquals("2015-01-30T12:00:00Z", ISO8601Format.format(((AfterFilter)filter.filters.get(1)).getDate())); + assertEquals(-1, filter.topSize); + assertFalse(filter.reverseOrder); + assertNull(filter.sortComp); + }catch(UWSException ue){ + ue.printStackTrace(System.err); + fail("No error should happen since all provided AFTER parameters are correct."); + } + + /* ************ */ + /* FILTER: LAST */ + + // 1 INCORRECT LAST PARAMETER => nothing should be set! + try{ + request.clearParams(); + request.addParams("LAST", "-10"); + filter = new JobListRefiner(request); + fail("The provided parameter LAST is NOT positive. An error should have occurred."); + }catch(UWSException ue){ + assertEquals("Incorrect LAST value: \"-10\"! A positive integer was expected.", ue.getMessage()); + assertEquals(UWSException.BAD_REQUEST, ue.getHttpErrorCode()); + } + try{ + request.clearParams(); + request.addParams("LAST", "foo"); + filter = new JobListRefiner(request); + fail("The provided parameter LAST is NOT an integer. An error should have occurred."); + }catch(UWSException ue){ + assertEquals("Incorrect LAST value: \"foo\"! A positive integer was expected.", ue.getMessage()); + assertEquals(UWSException.BAD_REQUEST, ue.getHttpErrorCode()); + } + + try{ + // CORRECT 1 LAST PARAMETER => topSize, sortComp and reverseOrder should be set! + request.clearParams(); + request.addParams("LAST", "10"); + filter = new JobListRefiner(request); + assertEquals(1, filter.filters.size()); + assertEquals(NoArchivedFilter.class, filter.filters.get(0).getClass()); + assertEquals(10, filter.topSize); + assertFalse(filter.reverseOrder); + assertNotNull(filter.sortComp); + assertEquals(JobListRefiner.JobComparator.class, filter.sortComp.getClass()); + + // SPECIAL CASE OF LAST=0 => same behavior as for a positive value + request.clearParams(); + request.addParams("LAST", "0"); + filter = new JobListRefiner(request); + assertEquals(1, filter.filters.size()); + assertEquals(NoArchivedFilter.class, filter.filters.get(0).getClass()); + assertEquals(0, filter.topSize); + assertFalse(filter.reverseOrder); + assertNotNull(filter.sortComp); + assertEquals(JobListRefiner.JobComparator.class, filter.sortComp.getClass()); + + // CORRECT 3 LAST PARAMETERS => Only the smallest value should be kept ; a StartedFilter should be set, as well as topSize, sortComp and reverseOrder! + request.clearParams(); + request.addParams("LAST", "10"); + request.addParams("last", "5"); + request.addParams("LAST", "7"); + filter = new JobListRefiner(request); + assertEquals(1, filter.filters.size()); + assertEquals(NoArchivedFilter.class, filter.filters.get(0).getClass()); + assertEquals(5, filter.topSize); + assertFalse(filter.reverseOrder); + assertNotNull(filter.sortComp); + assertEquals(JobListRefiner.JobComparator.class, filter.sortComp.getClass()); + }catch(UWSException ue){ + ue.printStackTrace(System.err); + fail("No error should happen since all provided LAST parameters are correct."); + } + + /* ************* */ + /* FILTER: PHASE */ + + // 1 INCORRECT PHASE PARAMETER => a NoArchivedFilter should be set! + try{ + request.clearParams(); + request.addParams("PHASE", "foo"); + filter = new JobListRefiner(request); + fail("The provided parameter PHASE is NOT a valid execution phase. An error should have occurred."); + }catch(UWSException ue){ + assertEquals("Incorrect PHASE value: \"foo\"! No such execution phase is known by this service.", ue.getMessage()); + assertEquals(UWSException.BAD_REQUEST, ue.getHttpErrorCode()); + } + + // CORRECT 1 CORRECT + 1 INCORRECT PHASE PARAMETER => a PhasesFilter with only the correct phase should be set! + try{ + request.clearParams(); + request.addParams("PHASE", "foo"); + request.addParams("phase", "EXECUTING"); + filter = new JobListRefiner(request); + assertEquals(1, filter.filters.size()); + assertEquals(PhasesFilter.class, filter.filters.get(0).getClass()); + assertEquals(1, ((PhasesFilter)filter.filters.get(0)).phases.size()); + assertEquals(ExecutionPhase.EXECUTING, ((PhasesFilter)filter.filters.get(0)).phases.get(0)); + assertEquals(-1, filter.topSize); + assertFalse(filter.reverseOrder); + assertNull(filter.sortComp); + }catch(UWSException ue){ + assertEquals("Incorrect PHASE value: \"foo\"! No such execution phase is known by this service.", ue.getMessage()); + assertEquals(UWSException.BAD_REQUEST, ue.getHttpErrorCode()); + } + + try{ + // CORRECT PHASE PARAMETER => a PhasesFilter should be set! + request.clearParams(); + request.addParams("PHASE", "EXECUTING"); + filter = new JobListRefiner(request); + assertEquals(1, filter.filters.size()); + assertEquals(PhasesFilter.class, filter.filters.get(0).getClass()); + assertEquals(1, ((PhasesFilter)filter.filters.get(0)).phases.size()); + assertEquals(ExecutionPhase.EXECUTING, ((PhasesFilter)filter.filters.get(0)).phases.get(0)); + assertEquals(-1, filter.topSize); + assertFalse(filter.reverseOrder); + assertNull(filter.sortComp); + + // CORRECT 2 CORRECT PHASE PARAMETERS => a PhasesFilter with the two correct phases should be set! + request.clearParams(); + request.addParams("PHASE", "QUEUED"); + request.addParams("phase", "EXECUTING"); + filter = new JobListRefiner(request); + assertEquals(1, filter.filters.size()); + assertEquals(PhasesFilter.class, filter.filters.get(0).getClass()); + assertEquals(2, ((PhasesFilter)filter.filters.get(0)).phases.size()); + assertEquals(ExecutionPhase.QUEUED, ((PhasesFilter)filter.filters.get(0)).phases.get(0)); + assertEquals(ExecutionPhase.EXECUTING, ((PhasesFilter)filter.filters.get(0)).phases.get(1)); + assertEquals(-1, filter.topSize); + assertFalse(filter.reverseOrder); + assertNull(filter.sortComp); + }catch(UWSException ue){ + ue.printStackTrace(System.err); + fail("No error should happen since all provided PHASE parameters are correct."); + } + + /* *********** */ + /* FILTER: ALL */ + + // ALL MIXED PARAMETERS + try{ + request.clearParams(); + request.addParams("last", "5"); + request.addParams("phase", "EXECUTING"); + request.addParams("AFTER", "2015-02-10T12:00:00"); + request.addParams("AFTER", "2013-01-10T12:00:00"); + filter = new JobListRefiner(request); + assertEquals(2, filter.filters.size()); + assertEquals(PhasesFilter.class, filter.filters.get(0).getClass()); + assertEquals(1, ((PhasesFilter)filter.filters.get(0)).phases.size()); + assertEquals(ExecutionPhase.EXECUTING, ((PhasesFilter)filter.filters.get(0)).phases.get(0)); + assertEquals(AfterFilter.class, filter.filters.get(1).getClass()); + assertEquals("2015-02-10T12:00:00Z", ISO8601Format.format(((AfterFilter)filter.filters.get(1)).getDate())); + assertEquals(5, filter.topSize); + assertFalse(filter.reverseOrder); + assertNotNull(filter.sortComp); + }catch(UWSException ue){ + ue.printStackTrace(System.err); + fail("No error should happen since all provided PHASE, LAST and AFTER parameters are correct."); + } + } + + @Test + public void testFilter(){ + ArrayList<UWSJob> jobs = new ArrayList<UWSJob>(10); + try{ + // 0 -> PENDING + UWSJob test = new UWSJob("0", (new Date()).getTime(), null, new UWSParameters(), -1, -1, -1, null, null); + jobs.add(test); + // 1 -> QUEUED + test = new UWSJob("1", (new Date()).getTime(), null, new UWSParameters(), -1, -1, -1, null, null); + test.setPhase(ExecutionPhase.QUEUED, true); + jobs.add(test); + // 2 -> ABORTED + test = new UWSJob("2", (new GregorianCalendar(2010, 2, 1)).getTimeInMillis(), null, new UWSParameters(), -1, (new GregorianCalendar(2010, 2, 1)).getTimeInMillis(), -1, null, null); + test.setPhase(ExecutionPhase.ABORTED, true); + jobs.add(test); + // 3 -> ARCHIVED + test = new UWSJob("3", (new GregorianCalendar(2010, 1, 12)).getTimeInMillis(), null, new UWSParameters(), -1, (new GregorianCalendar(2010, 1, 12)).getTimeInMillis(), (new GregorianCalendar(2010, 1, 13)).getTimeInMillis(), null, null); + test.setPhase(ExecutionPhase.ARCHIVED, true); + jobs.add(test); + // 4 -> EXECUTING + test = new UWSJob("4", (new GregorianCalendar(2015, 2, 2)).getTimeInMillis(), null, new UWSParameters(), -1, (new GregorianCalendar(2015, 2, 2)).getTimeInMillis(), -1, null, null); + test.setPhase(ExecutionPhase.EXECUTING, true); + jobs.add(test); + // 5 -> EXECUTING + test = new UWSJob("5", (new GregorianCalendar(2014, 8, 2)).getTimeInMillis(), null, new UWSParameters(), -1, (new GregorianCalendar(2014, 8, 2)).getTimeInMillis(), -1, null, null); + test.setPhase(ExecutionPhase.EXECUTING, true); + jobs.add(test); + // 6 -> ERROR + test = new UWSJob("6", (new GregorianCalendar(2015, 3, 3)).getTimeInMillis(), null, new UWSParameters(), -1, (new GregorianCalendar(2015, 3, 3)).getTimeInMillis(), (new GregorianCalendar(2015, 9, 4)).getTimeInMillis(), null, new ErrorSummary("", ErrorType.FATAL)); + test.setPhase(ExecutionPhase.ERROR, true); + jobs.add(test); + // 7 -> ERROR + test = new UWSJob("7", (new GregorianCalendar(2015, 8, 3)).getTimeInMillis(), null, new UWSParameters(), -1, (new GregorianCalendar(2015, 8, 3)).getTimeInMillis(), (new GregorianCalendar(2015, 9, 4)).getTimeInMillis(), null, new ErrorSummary("", ErrorType.FATAL)); + test.setPhase(ExecutionPhase.EXECUTING, true); + jobs.add(test); + // 8 -> ERROR + test = new UWSJob("8", (new GregorianCalendar(2015, 2, 3)).getTimeInMillis(), null, new UWSParameters(), -1, (new GregorianCalendar(2015, 2, 3)).getTimeInMillis(), (new GregorianCalendar(2015, 9, 4)).getTimeInMillis(), null, new ErrorSummary("", ErrorType.FATAL)); + test.setPhase(ExecutionPhase.EXECUTING, true); + jobs.add(test); + // 9 -> ERROR + test = new UWSJob("9", (new GregorianCalendar(2015, 1, 3)).getTimeInMillis(), null, new UWSParameters(), -1, (new GregorianCalendar(2015, 1, 3)).getTimeInMillis(), (new GregorianCalendar(2015, 9, 4)).getTimeInMillis(), null, new ErrorSummary("", ErrorType.FATAL)); + test.setPhase(ExecutionPhase.EXECUTING, true); + jobs.add(test); + }catch(UWSException ex){ + ex.printStackTrace(); + fail("Can not force the execution phase of the job! (see console for more details)"); + } + + /* ****************************** */ + /* No filter, no sort, no reverse */ + JobListRefiner filter = new JobListRefiner(); + + Iterator<UWSJob> it = filter.refine(jobs.iterator()); + for(int i = 0; i < 10; i++){ + assertTrue(it.hasNext()); + assertEquals("" + i, it.next().getJobId()); + } + assertFalse(it.hasNext()); + + /* ************************************** */ + /* 1 PHASE filter, no sort, no reverse */ + PhasesFilter pFilter = new PhasesFilter(ExecutionPhase.EXECUTING); + pFilter.add(ExecutionPhase.ARCHIVED); + filter.filters.add(pFilter); + + it = filter.refine(jobs.iterator()); + assertTrue(it.hasNext()); + assertEquals("3", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("4", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("5", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("7", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("8", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("9", it.next().getJobId()); + assertFalse(it.hasNext()); + + /* **************************************************** */ + /* 1 PHASE filter + 1 AFTER filter, no sort, no reverse */ + filter.filters.add(new AfterFilter((new GregorianCalendar(2015, 1, 1)).getTime())); + + it = filter.refine(jobs.iterator()); + assertTrue(it.hasNext()); + assertEquals("4", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("7", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("8", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("9", it.next().getJobId()); + assertFalse(it.hasNext()); + + /* ***************************************************************** */ + /* 1 PHASE filter + 1 AFTER filter, 3 first jobs, no sort, no reverse */ + filter.jobList.clear(); + filter.topSize = 3; + + it = filter.refine(jobs.iterator()); + assertTrue(it.hasNext()); + assertEquals("4", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("7", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("8", it.next().getJobId()); + assertFalse(it.hasNext()); + + /* ************************************************************************ */ + /* 1 PHASE filter + 1 AFTER filter, all jobs, sort by startTime, no reverse */ + filter.topSize = -1; + filter.sortComp = new Comparator<UWSJob>(){ + @Override + public int compare(UWSJob o1, UWSJob o2){ + return o1.getStartTime().compareTo(o2.getStartTime()); + } + }; + + it = filter.refine(jobs.iterator()); + assertTrue(it.hasNext()); + assertEquals("9", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("4", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("8", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("7", it.next().getJobId()); + assertFalse(it.hasNext()); + + /* ********************************************************************************** */ + /* 1 PHASE filter + 1 AFTER filter, the 4 first jobs, sort by startTime, WITH reverse */ + filter.topSize = 4; + filter.sortComp = new Comparator<UWSJob>(){ + @Override + public int compare(UWSJob o1, UWSJob o2){ + return o1.getStartTime().compareTo(o2.getStartTime()); + } + }; + filter.reverseOrder = true; + + it = filter.refine(jobs.iterator()); + assertTrue(it.hasNext()); + assertEquals("7", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("8", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("4", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("9", it.next().getJobId()); + assertFalse(it.hasNext()); + + /* ************************************************************************************************************ */ + /* 1 PHASE filter + 1 AFTER filter, 3 last jobs, sort by descending creationTime => simulation of LAST "filter" */ + filter.topSize = 3; + filter.sortComp = new Comparator<UWSJob>(){ + @Override + public int compare(UWSJob o1, UWSJob o2){ + return -(o1.getCreationTime().compareTo(o2.getCreationTime())); + } + }; + filter.reverseOrder = false; + + it = filter.refine(jobs.iterator()); + assertTrue(it.hasNext()); + assertEquals("7", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("8", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("4", it.next().getJobId()); + assertFalse(it.hasNext()); + + /* ***************************************************************************************************************** */ + /* No filter (except to avoid ARCHIVED), 3 last jobs, sort by descending creationTime => simulation of LAST "filter" */ + filter.filters.clear(); + filter.filters.add(new NoArchivedFilter()); + filter.topSize = 3; + filter.sortComp = new Comparator<UWSJob>(){ + @Override + public int compare(UWSJob o1, UWSJob o2){ + return -(o1.getCreationTime().compareTo(o2.getCreationTime())); + } + }; + filter.reverseOrder = false; + + it = filter.refine(jobs.iterator()); + assertTrue(it.hasNext()); + assertEquals("1", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("0", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("7", it.next().getJobId()); + assertFalse(it.hasNext()); + + /* *********************************************************************************************************************************************** */ + /* No filter (except to avoid ARCHIVED), 0 last jobs, sort by descending creationTime => simulation of LAST "filter" WITH THE SPECIAL VALUE LAST=0 */ + filter.filters.clear(); + filter.filters.add(new NoArchivedFilter()); + filter.topSize = 0; + filter.sortComp = new Comparator<UWSJob>(){ + @Override + public int compare(UWSJob o1, UWSJob o2){ + return -(o1.getCreationTime().compareTo(o2.getCreationTime())); + } + }; + filter.reverseOrder = false; + + it = filter.refine(jobs.iterator()); + assertFalse(it.hasNext()); + } + + @Test + public void testAddJob(){ + JobListRefiner filter = new JobListRefiner(); + filter.sortComp = new Comparator<UWSJob>(){ + @Override + public int compare(UWSJob o1, UWSJob o2){ + return o1.getCreationTime().compareTo(o2.getCreationTime()); + } + }; + + filter.addJob(new UWSJob("123456", (new GregorianCalendar(2013, 3, 10)).getTimeInMillis(), null, new UWSParameters(), -1, -1, -1, null, null)); + filter.addJob(new UWSJob("654321", (new GregorianCalendar(2010, 3, 10)).getTimeInMillis(), null, new UWSParameters(), -1, -1, -1, null, null)); + + assertEquals("654321", filter.jobList.get(0).getJobId()); + assertEquals("123456", filter.jobList.get(1).getJobId()); + } + + @Test + public void testMatch(){ + JobListRefiner filter = new JobListRefiner(); + + /* *********** */ + /* No filter */ + + // No job => Nope! + assertFalse(filter.match(null)); + // A job => Yes! + assertTrue(filter.match(new UWSJob(new UWSParameters()))); + + /* ********************************************** */ + /* Only jobs in EXECUTING phase */ + filter.filters.add(new PhasesFilter(ExecutionPhase.EXECUTING)); + + // No job => Nope! + assertFalse(filter.match(null)); + // Not an EXECUTING job => Nope! + assertFalse(filter.match(new UWSJob(new UWSParameters()))); + + UWSJob testJob = new UWSJob("123456", (new Date()).getTime(), null, new UWSParameters(), -1, (new Date()).getTime(), -1, null, null); + try{ + // EXECUTING job => OK! + testJob.setPhase(ExecutionPhase.EXECUTING, true); + assertTrue(filter.match(testJob)); + + // ARCHIVED job => Nope! + testJob.setPhase(ExecutionPhase.ARCHIVED, true); + assertFalse(filter.match(testJob)); + + // ERROR job => Nope! + testJob.setPhase(ExecutionPhase.ERROR, true); + assertFalse(filter.match(testJob)); + }catch(UWSException ex){ + ex.printStackTrace(); + fail("Can not force the execution phase of the job! (see console for more details)"); + } + + /* ********************************************************** */ + /* Only jobs in EXECUTING or ARCHIVED phase */ + ((PhasesFilter)filter.filters.get(filter.filters.size() - 1)).add(ExecutionPhase.ARCHIVED); + + // No job => Nope! + assertFalse(filter.match(null)); + // Not started job => Nope! + assertFalse(filter.match(new UWSJob(new UWSParameters()))); + + testJob = new UWSJob("123456", (new Date()).getTime(), null, new UWSParameters(), -1, (new Date()).getTime(), -1, null, null); + try{ + // EXECUTING job => OK! + testJob.setPhase(ExecutionPhase.EXECUTING, true); + assertTrue(filter.match(testJob)); + + // ARCHIVED job => OK! + testJob.setPhase(ExecutionPhase.ARCHIVED, true); + assertTrue(filter.match(testJob)); + + // ERROR job => Nope! + testJob.setPhase(ExecutionPhase.ERROR, true); + assertFalse(filter.match(testJob)); + }catch(UWSException ex){ + ex.printStackTrace(); + fail("Can not force the execution phase of the job! (see console for more details)"); + } + + /* ***************************************************************************** */ + /* Only jobs in EXECUTING or ARCHIVED phase AND after 1/3/2010 */ + filter.filters.add(new AfterFilter((new GregorianCalendar(2010, 2, 1)).getTime())); + + // No job => Nope! + assertFalse(filter.match(null)); + // Not started job => Nope! + assertFalse(filter.match(new UWSJob(new UWSParameters()))); + + testJob = new UWSJob("123456", (new GregorianCalendar(2010, 2, 2)).getTimeInMillis(), null, new UWSParameters(), -1, (new GregorianCalendar(2010, 2, 2)).getTimeInMillis(), -1, null, null); + try{ + // EXECUTING job, and after 1/3/2010 => OK! + testJob.setPhase(ExecutionPhase.EXECUTING, true); + assertTrue(filter.match(testJob)); + + // ARCHIVED job, and after 1/3/2010 => OK! + testJob.setPhase(ExecutionPhase.ARCHIVED, true); + assertTrue(filter.match(testJob)); + + // ERROR job, and after 1/3/2010 => Nope! + testJob.setPhase(ExecutionPhase.ERROR, true); + assertFalse(filter.match(testJob)); + }catch(UWSException ex){ + ex.printStackTrace(); + fail("Can not force the execution phase of the job! (see console for more details)"); + } + + testJob = new UWSJob("123456", (new GregorianCalendar(2010, 1, 1)).getTimeInMillis(), null, new UWSParameters(), -1, (new GregorianCalendar(2010, 1, 1)).getTimeInMillis(), -1, null, null); + try{ + // EXECUTING job, and before 1/3/2010 => Nope! + testJob.setPhase(ExecutionPhase.EXECUTING, true); + assertFalse(filter.match(testJob)); + + // ARCHIVED job, and before 1/3/2010 => Nope! + testJob.setPhase(ExecutionPhase.ARCHIVED, true); + assertFalse(filter.match(testJob)); + + // ERROR job, and before 1/3/2010 => Nope! + testJob.setPhase(ExecutionPhase.ERROR, true); + assertFalse(filter.match(testJob)); + }catch(UWSException ex){ + ex.printStackTrace(); + fail("Can not force the execution phase of the job! (see console for more details)"); + } + } + + @Test + public void testTopIterator(){ + ArrayList<UWSJob> jobs = new ArrayList<UWSJob>(5); + jobs.add(new UWSJob("0", (new Date()).getTime(), null, new UWSParameters(), -1, -1, -1, null, null)); + jobs.add(new UWSJob("1", (new Date()).getTime(), null, new UWSParameters(), -1, -1, -1, null, null)); + jobs.add(new UWSJob("2", (new Date()).getTime(), null, new UWSParameters(), -1, -1, -1, null, null)); + jobs.add(new UWSJob("3", (new Date()).getTime(), null, new UWSParameters(), -1, -1, -1, null, null)); + jobs.add(new UWSJob("4", (new Date()).getTime(), null, new UWSParameters(), -1, -1, -1, null, null)); + + // Just the 3 first items in same order: + TopIterator it = new TopIterator(jobs, 3, false); + assertTrue(it.hasNext()); + assertEquals("0", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("1", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("2", it.next().getJobId()); + assertFalse(it.hasNext()); + + // Just the 3 first items in reverse order: + it = new TopIterator(jobs, 3, true); + assertTrue(it.hasNext()); + assertEquals("2", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("1", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("0", it.next().getJobId()); + assertFalse(it.hasNext()); + + // All items in same order: + it = new TopIterator(jobs, -1, false); + assertTrue(it.hasNext()); + assertEquals("0", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("1", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("2", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("3", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("4", it.next().getJobId()); + assertFalse(it.hasNext()); + + // All items in reverse order: + it = new TopIterator(jobs, -1, true); + assertTrue(it.hasNext()); + assertEquals("4", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("3", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("2", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("1", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("0", it.next().getJobId()); + assertFalse(it.hasNext()); + + // More items than in the list in same order: + it = new TopIterator(jobs, 10, false); + assertTrue(it.hasNext()); + assertEquals("0", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("1", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("2", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("3", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("4", it.next().getJobId()); + assertFalse(it.hasNext()); + + // More items than in the list in reverse order: + it = new TopIterator(jobs, 10, true); + assertTrue(it.hasNext()); + assertEquals("4", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("3", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("2", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("1", it.next().getJobId()); + assertTrue(it.hasNext()); + assertEquals("0", it.next().getJobId()); + assertFalse(it.hasNext()); + } + + protected final static class TestHttpServletRequest implements HttpServletRequest { + + private HashMap<String,String[]> parameters = new HashMap<String,String[]>(); + + private static class NamesEnumeration implements Enumeration<String> { + + private final Iterator<String> it; + + public NamesEnumeration(final Set<String> names){ + this.it = names.iterator(); + } + + @Override + public boolean hasMoreElements(){ + return it.hasNext(); + } + + @Override + public String nextElement(){ + return it.next(); + } + + } + + public void addParams(final String name, final String value){ + if (parameters.containsKey(name)){ + String[] values = parameters.get(name); + values = Arrays.copyOf(values, values.length + 1); + values[values.length - 1] = value; + parameters.put(name, values); + }else + parameters.put(name, new String[]{value}); + } + + public void clearParams(){ + parameters.clear(); + } + + @Override + public Enumeration<String> getParameterNames(){ + return new NamesEnumeration(parameters.keySet()); + } + + @Override + public String[] getParameterValues(String name){ + return parameters.get(name); + } + + @Override + public Map<String,String[]> getParameterMap(){ + return parameters; + } + + @Override + public String getParameter(String name){ + String[] values = parameters.get(name); + if (values == null || values.length == 0) + return null; + else + return values[0]; + } + + @Override + public AsyncContext startAsync(ServletRequest arg0, ServletResponse arg1){ + return null; + } + + @Override + public AsyncContext startAsync(){ + return null; + } + + @Override + public void setCharacterEncoding(String arg0) throws UnsupportedEncodingException{ + + } + + @Override + public void setAttribute(String arg0, Object arg1){ + + } + + @Override + public void removeAttribute(String arg0){ + + } + + @Override + public boolean isSecure(){ + return false; + } + + @Override + public boolean isAsyncSupported(){ + return false; + } + + @Override + public boolean isAsyncStarted(){ + return false; + } + + @Override + public ServletContext getServletContext(){ + return null; + } + + @Override + public int getServerPort(){ + return 0; + } + + @Override + public String getServerName(){ + return null; + } + + @Override + public String getScheme(){ + return null; + } + + @Override + public RequestDispatcher getRequestDispatcher(String arg0){ + return null; + } + + @Override + public int getRemotePort(){ + return 0; + } + + @Override + public String getRemoteHost(){ + return null; + } + + @Override + public String getRemoteAddr(){ + return null; + } + + @Override + public String getRealPath(String arg0){ + return null; + } + + @Override + public BufferedReader getReader() throws IOException{ + return null; + } + + @Override + public String getProtocol(){ + return null; + } + + @Override + public Enumeration<Locale> getLocales(){ + return null; + } + + @Override + public Locale getLocale(){ + return null; + } + + @Override + public int getLocalPort(){ + return 0; + } + + @Override + public String getLocalName(){ + return null; + } + + @Override + public String getLocalAddr(){ + return null; + } + + @Override + public ServletInputStream getInputStream() throws IOException{ + return null; + } + + @Override + public DispatcherType getDispatcherType(){ + return null; + } + + @Override + public String getContentType(){ + return null; + } + + @Override + public int getContentLength(){ + return 0; + } + + @Override + public String getCharacterEncoding(){ + return null; + } + + @Override + public Enumeration<String> getAttributeNames(){ + return null; + } + + @Override + public Object getAttribute(String arg0){ + return null; + } + + @Override + public AsyncContext getAsyncContext(){ + return null; + } + + @Override + public void logout() throws ServletException{} + + @Override + public void login(String arg0, String arg1) throws ServletException{} + + @Override + public boolean isUserInRole(String arg0){ + return false; + } + + @Override + public boolean isRequestedSessionIdValid(){ + return false; + } + + @Override + public boolean isRequestedSessionIdFromUrl(){ + return false; + } + + @Override + public boolean isRequestedSessionIdFromURL(){ + return false; + } + + @Override + public boolean isRequestedSessionIdFromCookie(){ + return false; + } + + @Override + public Principal getUserPrincipal(){ + return null; + } + + @Override + public HttpSession getSession(boolean arg0){ + return null; + } + + @Override + public HttpSession getSession(){ + return null; + } + + @Override + public String getServletPath(){ + return null; + } + + @Override + public String getRequestedSessionId(){ + return null; + } + + @Override + public StringBuffer getRequestURL(){ + return null; + } + + @Override + public String getRequestURI(){ + return null; + } + + @Override + public String getRemoteUser(){ + return null; + } + + @Override + public String getQueryString(){ + return null; + } + + @Override + public String getPathTranslated(){ + return null; + } + + @Override + public String getPathInfo(){ + return null; + } + + @Override + public Collection<Part> getParts() throws IOException, IllegalStateException, ServletException{ + return null; + } + + @Override + public Part getPart(String arg0) throws IOException, IllegalStateException, ServletException{ + return null; + } + + @Override + public String getMethod(){ + return "GET"; + } + + @Override + public int getIntHeader(String arg0){ + return 0; + } + + @Override + public Enumeration<String> getHeaders(String arg0){ + return null; + } + + @Override + public Enumeration<String> getHeaderNames(){ + return null; + } + + @Override + public String getHeader(String arg0){ + return null; + } + + @Override + public long getDateHeader(String arg0){ + return 0; + } + + @Override + public Cookie[] getCookies(){ + return null; + } + + @Override + public String getContextPath(){ + return null; + } + + @Override + public String getAuthType(){ + return null; + } + + @Override + public boolean authenticate(HttpServletResponse arg0) throws IOException, ServletException{ + return false; + } + + } + +} \ No newline at end of file diff --git a/test/uws/job/serializer/filter/TestNoArchivedFilter.java b/test/uws/job/serializer/filter/TestNoArchivedFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..a4d89cfa5409ab94caa5bd8db083c6446735cf0b --- /dev/null +++ b/test/uws/job/serializer/filter/TestNoArchivedFilter.java @@ -0,0 +1,71 @@ +package uws.job.serializer.filter; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.junit.Test; + +import uws.UWSException; +import uws.job.ExecutionPhase; +import uws.job.UWSJob; +import uws.job.parameters.UWSParameters; + +public class TestNoArchivedFilter { + + @Test + public void testMatch(){ + NoArchivedFilter filter = new NoArchivedFilter(); + + // No job => Nope! + assertFalse(filter.match(null)); + + UWSJob testJob = new UWSJob(new UWSParameters()); + + // Job PENDING => OK! + assertTrue(filter.match(testJob)); + + try{ + // Job QUEUED => OK! + testJob.setPhase(ExecutionPhase.QUEUED, true); + assertTrue(filter.match(testJob)); + + // Job HELD => OK! + testJob.setPhase(ExecutionPhase.HELD, true); + assertTrue(filter.match(testJob)); + + // Job SUSPENDED => OK! + testJob.setPhase(ExecutionPhase.SUSPENDED, true); + assertTrue(filter.match(testJob)); + + // Job EXECUTING => OK! + testJob.setPhase(ExecutionPhase.EXECUTING, true); + assertTrue(filter.match(testJob)); + + // Job ERROR => OK! + testJob.setPhase(ExecutionPhase.ERROR, true); + assertTrue(filter.match(testJob)); + + // Job ABORTED => OK! + testJob.setPhase(ExecutionPhase.ABORTED, true); + assertTrue(filter.match(testJob)); + + // Job COMPLETED => OK! + testJob.setPhase(ExecutionPhase.COMPLETED, true); + assertTrue(filter.match(testJob)); + + // Job UNKNOWN => OK! + testJob.setPhase(ExecutionPhase.UNKNOWN, true); + assertTrue(filter.match(testJob)); + + // Job ARCHIVED => Nope! + testJob.setPhase(ExecutionPhase.ARCHIVED, true); + assertFalse(filter.match(testJob)); + + }catch(UWSException e){ + e.printStackTrace(); + fail("Impossible to change the phase! (see console for more details)"); + } + } + +} \ No newline at end of file diff --git a/test/uws/job/serializer/filter/TestPhasesFilter.java b/test/uws/job/serializer/filter/TestPhasesFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..cec6f7780e43b8801f6e3e2a8d45179c473acc06 --- /dev/null +++ b/test/uws/job/serializer/filter/TestPhasesFilter.java @@ -0,0 +1,125 @@ +package uws.job.serializer.filter; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.junit.Test; + +import uws.UWSException; +import uws.job.ExecutionPhase; +import uws.job.UWSJob; +import uws.job.parameters.UWSParameters; + +public class TestPhasesFilter { + + @Test + public void testMatch(){ + PhasesFilter filter = new PhasesFilter(ExecutionPhase.EXECUTING); + + /* FILTER WITH ONLY ONE PHASE: EXECUTING */ + + // No job => Nope! + assertFalse(filter.match(null)); + + // Job PENDING => Nope! + UWSJob testJob = new UWSJob(new UWSParameters()); + assertFalse(filter.match(testJob)); + + // Job EXECUTING => OK! + try{ + testJob.setPhase(ExecutionPhase.EXECUTING, true); + assertTrue(filter.match(testJob)); + }catch(UWSException e){ + e.printStackTrace(); + fail("Impossible to change the phase! (see console for more details)"); + } + + /* FILTER WITH TWO PHASES: QUEUED & EXECUTING */ + filter.add(ExecutionPhase.QUEUED); + + // Job PENDING => Nope! + testJob = new UWSJob(new UWSParameters()); + assertFalse(filter.match(testJob)); + + // Job QUEUED => OK! + try{ + testJob.setPhase(ExecutionPhase.QUEUED, true); + assertTrue(filter.match(testJob)); + }catch(UWSException e){ + e.printStackTrace(); + fail("Impossible to change the phase! (see console for more details)"); + } + + // Job EXECUTING => OK! + try{ + testJob.setPhase(ExecutionPhase.EXECUTING, true); + assertTrue(filter.match(testJob)); + }catch(UWSException e){ + e.printStackTrace(); + fail("Impossible to change the phase! (see console for more details)"); + } + + // Job ARCHIVED => Nope! + try{ + testJob.setPhase(ExecutionPhase.ARCHIVED, true); + assertFalse(filter.match(testJob)); + }catch(UWSException e){ + e.printStackTrace(); + fail("Impossible to change the phase! (see console for more details)"); + } + + // Job ABORTED => Nope! + try{ + testJob.setPhase(ExecutionPhase.ABORTED, true); + assertFalse(filter.match(testJob)); + }catch(UWSException e){ + e.printStackTrace(); + fail("Impossible to change the phase! (see console for more details)"); + } + + /* FILTER WITH THREE PHASES: QUEUED & EXECUTING & ARCHIVED */ + filter.add(ExecutionPhase.ARCHIVED); + + // Job PENDING => Nope! + testJob = new UWSJob(new UWSParameters()); + assertFalse(filter.match(testJob)); + + // Job QUEUED => OK! + try{ + testJob.setPhase(ExecutionPhase.QUEUED, true); + assertTrue(filter.match(testJob)); + }catch(UWSException e){ + e.printStackTrace(); + fail("Impossible to change the phase! (see console for more details)"); + } + + // Job EXECUTING => OK! + try{ + testJob.setPhase(ExecutionPhase.EXECUTING, true); + assertTrue(filter.match(testJob)); + }catch(UWSException e){ + e.printStackTrace(); + fail("Impossible to change the phase! (see console for more details)"); + } + + // Job ARCHIVED => OK! + try{ + testJob.setPhase(ExecutionPhase.ARCHIVED, true); + assertTrue(filter.match(testJob)); + }catch(UWSException e){ + e.printStackTrace(); + fail("Impossible to change the phase! (see console for more details)"); + } + + // Job ABORTED => Nope! + try{ + testJob.setPhase(ExecutionPhase.ABORTED, true); + assertFalse(filter.match(testJob)); + }catch(UWSException e){ + e.printStackTrace(); + fail("Impossible to change the phase! (see console for more details)"); + } + } + +} \ No newline at end of file