package gw.rating.impact uses gw.api.locale.DisplayKey uses gw.api.tools.BatchProcess uses gw.api.webservice.pc.maintenanceTools.PCMaintenanceToolsImpl uses gw.api.webservice.maintenanceTools.WorkQueueStatus uses gw.api.webservice.maintenanceTools.ProcessStatus uses gw.api.database.Query uses gw.api.util.DisplayableException /** * Utilities for running and monitoring impact test batch processes * */ class ImpactBatchUtil { /** * Start the test prep batch process * @param testcase test case * @param reprocessErrorsOnly optional - reprocess periods that failed in the previous */ static function startTestPrepBatchProcess(testCase : ImpactTestingTestCase, reprocessErrorsOnly = false, continueFromLastProcess = false){ if (jobBeingProcessed() != null) { throw new DisplayableException(DisplayKey.get("Web.Rating.ImpactTesting.BatchJobAlreadyRunning", jobBeingProcessed().DisplayName)) } // Clear out any prior work items in this batch process clearQueue(BatchProcessType.TC_IMPACTTESTINGTESTPREP) testCase.refresh() gw.transaction.Transaction.runWithNewBundle(\ bundle -> { testCase = bundle.add(testCase) testCase.touch() // We touch to force change of the testCase's update time. Update time on the testcase is used to see if a // testcase has logically changed. // In this case, we are logically altering the overall test case by marking the testCase // periods ready for test run testCase.Periods .where(\ p -> (!reprocessErrorsOnly or p.TestPrepErrorMessage != null) and (!continueFromLastProcess or p.TestPrepProgress != TC_PROCESSED )) .each(\ p -> { p = bundle.add(p) p.refresh() p.markReadyForTestPrep() }) }) startBatchJob(TC_IMPACTTESTINGTESTPREP) } /** * Cancel the test prep batch process */ static function cancelTestPrepBatchProcess(){ cancelBatchJob(BatchProcessType.TC_IMPACTTESTINGTESTPREP) } /** * @return true if the test prep batch process is running * */ static function isTestPrepJobBeingProcessed() : boolean{ return isJobBeingProcessed(BatchProcessType.TC_IMPACTTESTINGTESTPREP) } /** * Start the test run batch process * @param testcase test case */ static function startTestRunBatchProcess(testCase : ImpactTestingTestCase, continueFromLastProcess = false){ if (jobBeingProcessed() != null) { throw new DisplayableException(DisplayKey.get("Web.Rating.ImpactTesting.BatchJobAlreadyRunning", jobBeingProcessed().DisplayName)) } // Clear out any prior work items in this batch process clearQueue(BatchProcessType.TC_IMPACTTESTINGTESTRUN) testCase.refresh() gw.transaction.Transaction.runWithNewBundle(\ bundle -> { testCase = bundle.add(testCase) testCase.touch() // We touch to force change of the testCase's update time. Update time on the testcase is used to see if a // testcase has logically changed. // In this case, we are logically altering the overall test case by marking the testCase // periods ready for test run testCase.Result = null // this line does not always trigger update of the update time (i.e. - when Result already == null testCase.Periods .where(\ p -> (!continueFromLastProcess or p.TestRunProgress != TC_PROCESSED )) .each(\ p -> { p = bundle.add(p) p.refresh() p.markReadyForTestRun() }) }) startBatchJob(BatchProcessType.TC_IMPACTTESTINGTESTRUN) } /** * Cancel the test run batch process */ static function cancelTestRunBatchProcess(){ cancelBatchJob(BatchProcessType.TC_IMPACTTESTINGTESTRUN) } /** * @return true if the test run batch process is running * */ static function isTestRunJobBeingProcessed() : boolean{ return isJobBeingProcessed(BatchProcessType.TC_IMPACTTESTINGTESTRUN) } /** * Start a batch job * @param type batch process type * */ static function startBatchJob(type : BatchProcessType) : void{ var batchProcess = new BatchProcess(type) batchProcess.start() var tools = new PCMaintenanceToolsImpl() tools.startWorkQueueWorkers(type.Code) } /** * Cancel a batch job * @param type batch process type * */ static function cancelBatchJob(type : BatchProcessType) : void{ var batchProcess = new BatchProcess(type) batchProcess.requestTermination() var tools = new PCMaintenanceToolsImpl() tools.stopWorkQueueWorkers(type.Code) } /** * @param type batch process type * @return true if items are being processed and processing has not been canceled */ static function isJobBeingProcessed(type : BatchProcessType) : boolean{ // Get a count directly from StandardWorkItem because the queueStatus reports an unreliable number of items var count = countOfActiveWorkItems(type) var queue = queueStatus(type) return (count > 0 and queue.NumActiveWorkers > 0) } /** * @return which batch job is being processed. null if none */ static function jobBeingProcessed(): BatchProcessType{ if (isJobBeingProcessed(TC_IMPACTTESTINGTESTPREP)) { return TC_IMPACTTESTINGTESTPREP } else if (isJobBeingProcessed(TC_IMPACTTESTINGTESTRUN)) { return TC_IMPACTTESTINGTESTRUN } else if (isJobBeingProcessed(TC_IMPACTTESTINGEXPORT)){ return TC_IMPACTTESTINGEXPORT } return null } /** * @return the count of active work items for a batch process */ static function countOfActiveWorkItems(type : BatchProcessType) : int{ // Note - we're intentionally not using WorkQueueStatus.NumActiveWorkerItems // as it appears to be reporting the number of active worker items for all queues // using the StandardWorkItem var query = Query.make(StandardWorkItem) query.compare(StandardWorkItem#QueueType, Equals, type) query.compareIn(StandardWorkItem#Status, {WorkItemStatusType.TC_CHECKEDOUT, WorkItemStatusType.TC_AVAILABLE}) return query.select().Count } /** * @param type batch process type * @return the queue status */ static function queueStatus(type : BatchProcessType) : WorkQueueStatus{ var tools = new PCMaintenanceToolsImpl() return tools.getWorkQueueStatus(type.Code) } /** * @param type batch process type * @return the batch process status */ static function batchProcessStatus(type : BatchProcessType) : ProcessStatus{ var tools = new PCMaintenanceToolsImpl() return tools.batchProcessStatusByName(type.Code) } /** * Clear a queue's work items */ static function clearQueue(type : BatchProcessType){ gw.transaction.Transaction.runWithNewBundle(\ bundle -> { var query = Query.make(StandardWorkItem) query.compare(StandardWorkItem#QueueType,Equals, type) query.select().each(\ s -> { bundle.delete(s) }) }, "su") } }