/** * Copyright © 2013 enioka. All rights reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.enioka.jqm.api; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; /** * Parameters for querying {@link JobInstance}s. A null parameter (the default for most query parameters) is ignored in the query. To query * a null String, specify "" (empty String). To query a null Integer, specify -1. It is not possible to query for null Calendar values, * since it is far more efficient to query by status (the different Calendar fields are only null at certain statuses).<br> * See individual setters for the signification of query parameters.<br> * <br> * By default, i.e. by simply using <code>Query.create().run()</code>, the API retrieves the first 50 instances that have ended, ordered by * launch ID (i.e. by time). See {@link Query#setQueryLiveInstances(boolean)} for details and how to retrieve living instances in addition * to ended ones.<br> * <br> * * Also please note that queries get more expensive with the result count, so it is <strong>strongly recommended to use pagination</strong> * ({@link #setFirstRow(Integer)} and {@link #setPageSize(Integer)}). * */ @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public final class Query { private Integer jobInstanceId, parentId; private List<String> applicationName = new ArrayList<String>(); private String user, sessionId; private String jobDefKeyword1, jobDefKeyword2, jobDefKeyword3, jobDefModule, jobDefApplication; private String instanceKeyword1, instanceKeyword2, instanceKeyword3, instanceModule, instanceApplication; private String queueName, nodeName; private Integer queueId; private Calendar enqueuedBefore, enqueuedAfter, beganRunningBefore, beganRunningAfter, endedBefore, endedAfter; @XmlElementWrapper(name = "statuses") @XmlElement(name = "status", type = State.class) private List<State> status = new ArrayList<State>(); private Integer firstRow, pageSize = 50; private Integer resultSize; @XmlElementWrapper(name = "instances") @XmlElement(name = "instance", type = JobInstance.class) private List<JobInstance> results; @XmlElementWrapper(name = "sortby") @XmlElement(name = "sortitem", type = SortSpec.class) private List<SortSpec> sorts = new ArrayList<Query.SortSpec>(); private boolean queryLiveInstances = false, queryHistoryInstances = true; /** * The different fields that can be used in sorting. */ public static enum Sort { ID("id"), APPLICATIONNAME("JD_KEY"), QUEUENAME("QUEUE_NAME"), STATUS("STATUS"), DATEENQUEUE("DATE_ENQUEUE"), DATEATTRIBUTION( "DATE_ATTRIBUTION"), DATEEXECUTION("DATE_START"), DATEEND("DATE_END", null), USERNAME("USERNAME"), PARENTID("PARENT"); private String historyField, jiField; private Sort(String historyField, String jiField) { this.historyField = historyField; this.jiField = jiField; } private Sort(String commonField) { this.historyField = commonField; this.jiField = commonField; } String getHistoryField() { return this.historyField; } String getJiField() { return this.jiField; } } /** * The sort order */ static enum SortOrder { ASCENDING, DESCENDING; } /** * Internal description of a sorting operation */ @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) static class SortSpec { SortOrder order = SortOrder.ASCENDING; Sort col; // Bean convention @SuppressWarnings("unused") private SortSpec() { } SortSpec(SortOrder order, Sort column) { this.order = order; this.col = column; } } /** * Adds a new column a the end of the sorting clause. * * @see #addSortDesc(Sort) */ public Query addSortAsc(Sort column) { this.sorts.add(new SortSpec(SortOrder.ASCENDING, column)); return this; } /** * Adds a new column a the end of the sorting clause. * * @see #addSortAsc(Sort) */ public Query addSortDesc(Sort column) { this.sorts.add(new SortSpec(SortOrder.DESCENDING, column)); return this; } List<SortSpec> getSorts() { return this.sorts; } // ////////////////////////////////////////// // Accelerator constructors // ////////////////////////////////////////// public Query() { } public Query(String userName) { this.user = userName; } public Query(String applicationName, String instanceKeyword1) { this.setApplicationName(applicationName); this.instanceKeyword1 = instanceKeyword1; } // ////////////////////////////////////////// // Builder // ////////////////////////////////////////// /** * The start of the fluent Query API. Creates a new Query object. */ public static Query create() { return new Query(); } /** * The end of the fluent Query API. It simply executes the query and returns the results. */ public List<JobInstance> run() { return JqmClientFactory.getClient().getJobs(this); } // ////////////////////////////////////////// // Results handling // ////////////////////////////////////////// /** * This sets the maximum returned results count, for pagination purposes.<br> * It is <strong>highly recommended to use pagination</strong> when using the Query API, since queries are expensive. * * @param pageSize * the maximal result count, or null for no limit (dangerous!) * @return the Query itself (fluent API - used to chain calls). * @see #setFirstRow(Integer) setFirstRow for the other pagination parameter. */ public Query setPageSize(Integer pageSize) { this.pageSize = pageSize; return this; } /** * This sets the starting row returned by the query, for pagination purposes. Note that even if order is very important for paginated * queries (to ensure that the pages stay the same between calls for new pages), a default sort is used if none is specified.<br> * It is <strong>highly recommended to use pagination</strong> when using the Query API, since queries are expensive. * * @param firstRow * the first row to return. 0 is equivalent to null. * @return the Query itself (fluent API - used to chain calls). * @see #setPageSize(Integer) setPageSize for the other pagination parameter. */ public Query setFirstRow(Integer firstRow) { this.firstRow = firstRow; return this; } /** * @return the available result count of the query. Available means that it does not take into account pagination. This is mostly used * when pagination is used, so as to be able to set a "total records count" or a "page 2 on 234" indicator. If pagination is not * used, this is always equal to <code>{@link #getResults()}.size()</code>. */ public Integer getResultSize() { if (results == null) { throw new IllegalStateException("Cannot retrieve the results of a query that was not run"); } if (resultSize != null) { return resultSize; } else { return results.size(); } } void setResultSize(Integer resultSize) { this.resultSize = resultSize; } public List<JobInstance> getResults() { if (results == null) { throw new IllegalStateException("Cannot retrieve the results of a query that was not run"); } return results; } void setResults(List<JobInstance> results) { this.results = results; } // ////////////////////////////////////////// // Stupid get/set // ////////////////////////////////////////// Integer getJobInstanceId() { return jobInstanceId; } /** * To query a specific job instance. This ID is returned, for example, by the {@link JqmClient#enqueue(JobRequest)} method. <br> * It is pretty useless to give any other query parameters if you know the ID. Also note that there is a shortcut method named * {@link JqmClient#getJob(int)} to make a query by ID. * * @param jobInstanceId * the job instance ID */ public Query setJobInstanceId(Integer jobInstanceId) { this.jobInstanceId = jobInstanceId; return this; } Integer getParentId() { return parentId; } /** * Some job instances are launched by other job instances (linked jobs which launch one another). This allows to query all job instances * launched by a specific job instance. * * @param parentId * the ID of the parent job instance. */ public Query setParentId(Integer parentId) { this.parentId = parentId; return this; } List<String> getApplicationName() { return applicationName; } /** * The application name is the name of the job definition - the same name that is given in the Job Definition XML. This allows to query * all job instances for given job definitions. If the list contains multiple names, an OR query takes place. * * @param applicationName */ public Query setApplicationName(List<String> applicationName) { this.applicationName = applicationName; return this; } /** * The application name is the name of the job definition - the same name that is given in the Job Definition XML. This allows to query * all job instances for a single given job definition. If other names were given previously (e.g. with * {@link #setApplicationName(List)} , they are removed by this method. * * @param applicationName * @return */ public Query setApplicationName(String applicationName) { this.applicationName.clear(); this.applicationName.add(applicationName); return this; } String getUser() { return user; } /** * Optionally, it is possible to specify some classification data at enqueue time (inside the {@link JobRequest} object). This data * exists solely for later querying (no signification whatsoever for JQM itself). This parameter allows such querying. * * @param user */ public Query setUser(String user) { this.user = user; return this; } String getSessionId() { return sessionId; } /** * Optionally, it is possible to specify some classification data at enqueue time (inside the {@link JobRequest} object). This data * exists solely for later querying (no signification whatsoever for JQM itself). This parameter allows such querying. * * @param sessionId */ public Query setSessionId(String sessionId) { this.sessionId = sessionId; return this; } String getJobDefKeyword1() { return jobDefKeyword1; } /** * Optionally, it is possible to specify some classification data inside the Job Definition (usually through the import of a JobDef XML * file). This data exists solely for later querying (no signification whatsoever for JQM itself). This parameter allows such querying. * * @param jobDefKeyword1 */ public Query setJobDefKeyword1(String jobDefKeyword1) { this.jobDefKeyword1 = jobDefKeyword1; return this; } String getJobDefKeyword2() { return jobDefKeyword2; } /** * Optionally, it is possible to specify some classification data inside the Job Definition (usually through the import of a JobDef XML * file). This data exists solely for later querying (no signification whatsoever for JQM itself). This parameter allows such querying. * * @param jobDefKeyword2 */ public Query setJobDefKeyword2(String jobDefKeyword2) { this.jobDefKeyword2 = jobDefKeyword2; return this; } String getJobDefKeyword3() { return jobDefKeyword3; } /** * Optionally, it is possible to specify some classification data inside the Job Definition (usually through the import of a JobDef XML * file). This data exists solely for later querying (no signification whatsoever for JQM itself). This parameter allows such querying. * * @param jobDefKeyword3 */ public Query setJobDefKeyword3(String jobDefKeyword3) { this.jobDefKeyword3 = jobDefKeyword3; return this; } String getJobDefModule() { return jobDefModule; } /** * Optionally, it is possible to specify some classification data inside the Job Definition (usually through the import of a JobDef XML * file). This data exists solely for later querying (no signification whatsoever for JQM itself). This parameter allows such querying. * * @param jobDefModule */ public Query setJobDefModule(String jobDefModule) { this.jobDefModule = jobDefModule; return this; } String getJobDefApplication() { return jobDefApplication; } /** * Optionally, it is possible to specify some classification data inside the Job Definition (usually through the import of a JobDef XML * file). This data exists solely for later querying (no signification whatsoever for JQM itself). This parameter allows such querying. * <br> * <strong>This has nothing to so with applicationName, which is the name of the Job Definition !</strong> * * @param jobDefApplication */ public Query setJobDefApplication(String jobDefApplication) { this.jobDefApplication = jobDefApplication; return this; } String getInstanceKeyword1() { return instanceKeyword1; } /** * Optionally, it is possible to specify some classification data at enqueue time (inside the {@link JobRequest} object). This data * exists solely for later querying (no signification whatsoever for JQM itself). This parameter allows such querying. * * @param instanceKeyword1 */ public Query setInstanceKeyword1(String instanceKeyword1) { this.instanceKeyword1 = instanceKeyword1; return this; } String getInstanceKeyword2() { return instanceKeyword2; } /** * Optionally, it is possible to specify some classification data at enqueue time (inside the {@link JobRequest} object). This data * exists solely for later querying (no signification whatsoever for JQM itself). This parameter allows such querying. * * @param instanceKeyword2 */ public Query setInstanceKeyword2(String instanceKeyword2) { this.instanceKeyword2 = instanceKeyword2; return this; } String getInstanceKeyword3() { return instanceKeyword3; } /** * Optionally, it is possible to specify some classification data at enqueue time (inside the {@link JobRequest} object). This data * exists solely for later querying (no signification whatsoever for JQM itself). This parameter allows such querying. * * @param instanceKeyword3 */ public Query setInstanceKeyword3(String instanceKeyword3) { this.instanceKeyword3 = instanceKeyword3; return this; } String getInstanceModule() { return instanceModule; } /** * Optionally, it is possible to specify some classification data at enqueue time (inside the {@link JobRequest} object). This data * exists solely for later querying (no signification whatsoever for JQM itself). This parameter allows such querying. * * @param instanceModule */ public Query setInstanceModule(String instanceModule) { this.instanceModule = instanceModule; return this; } String getInstanceApplication() { return instanceApplication; } /** * Optionally, it is possible to specify some classification data at enqueue time (inside the {@link JobRequest} object). This data * exists solely for later querying (no signification whatsoever for JQM itself). This parameter allows such querying. <br> * <strong>This has nothing to so with applicationName, which is the name of the Job Definition !</strong> * * @param instanceApplication */ public Query setInstanceApplication(String instanceApplication) { this.instanceApplication = instanceApplication; return this; } boolean isQueryLiveInstances() { return queryLiveInstances; } /** * By default, querying only occurs on ended (OK or not) job instances. If this parameter is set to true, it will also include living * (waiting, running, ...) job instances.<br> * If you also query on live instances at the same time, this will reset pagination as it is impossible to use pagination with both.<br> * <br> * Setting this to true has a noticeable performance impact and should be used as little as possible (or should be used when * {@link #setQueryHistoryInstances(boolean)} is false, which is not the default) */ public Query setQueryLiveInstances(boolean queryLiveInstances) { this.queryLiveInstances = queryLiveInstances; if (this.queryHistoryInstances) { this.pageSize = null; this.firstRow = null; } return this; } boolean isQueryHistoryInstances() { return queryHistoryInstances; } /** * By default, querying only occurs on ended (OK or not) job instances. If this parameter is set to false, however, the History will not * be used. This is usually used in conjunction with {@link #setQueryLiveInstances(boolean)}<br> * <br> */ public Query setQueryHistoryInstances(boolean queryHistoryInstances) { this.queryHistoryInstances = queryHistoryInstances; return this; } Calendar getEnqueuedBefore() { return enqueuedBefore; } /** * The time at which the execution request was given to {@link JqmClient#enqueue(JobRequest)}. This is an <= comparison. * * @param enqueuedBefore */ public Query setEnqueuedBefore(Calendar enqueuedBefore) { this.enqueuedBefore = enqueuedBefore; return this; } Calendar getEnqueuedAfter() { return enqueuedAfter; } /** * The time at which the execution request was given to {@link JqmClient#enqueue(JobRequest)}. This is an >= comparison. * * @param enqueuedAfter */ public Query setEnqueuedAfter(Calendar enqueuedAfter) { this.enqueuedAfter = enqueuedAfter; return this; } Calendar getBeganRunningBefore() { return beganRunningBefore; } /** * The time at which the execution really began (the request arrived at the top of the queue and was run by an engine). This is an <= * comparison. * * @param beganRunningBefore */ public Query setBeganRunningBefore(Calendar beganRunningBefore) { this.beganRunningBefore = beganRunningBefore; return this; } Calendar getBeganRunningAfter() { return beganRunningAfter; } /** * The time at which the execution really began (the request arrived at the top of the queue and was run by an engine). This is an >= * comparison. * * @param beganRunningAfter */ public Query setBeganRunningAfter(Calendar beganRunningAfter) { this.beganRunningAfter = beganRunningAfter; return this; } Calendar getEndedBefore() { return endedBefore; } /** * The time at which the execution ended, resulting in an ENDED or CRASHED status. This is an <= comparison. * * @param endedBefore */ public Query setEndedBefore(Calendar endedBefore) { this.endedBefore = endedBefore; return this; } Calendar getEndedAfter() { return endedAfter; } /** * The time at which the execution ended, resulting in an ENDED or CRASHED status. This is an <= comparison. * * @param endedAfter */ public Query setEndedAfter(Calendar endedAfter) { this.endedAfter = endedAfter; return this; } List<State> getStatus() { return status; } /** * Filter by status. See {@link State} for the different possible values and their meaning. If multiple values are added, a logical OR * will take place. * * @param status */ public Query addStatusFilter(State status) { this.status.add(status); return this; } Integer getFirstRow() { return firstRow; } Integer getPageSize() { return pageSize; } String getQueueName() { return queueName; } /** * For querying jobs on a given queue. The list of queues can be retrieved through {@link JqmClient#getQueues()}. */ public Query setQueueName(String queueName) { this.queueName = queueName; return this; } Integer getQueueId() { return queueId; } /** * For querying jobs on a given queue. The list of queues can be retrieved through {@link JqmClient#getQueues()}.<br> * Ignored if setQueueName is used. */ public Query setQueueId(Integer queueId) { this.queueId = queueId; return this; } /** * For querying jobs that have run or are running on a specific JQM node. */ public Query setNodeName(String nodeName) { this.nodeName = nodeName; return this; } String getNodeName() { return nodeName; } }