001/*- 002 * #%L 003 * HAPI FHIR JPA Server - Batch2 Task Processor 004 * %% 005 * Copyright (C) 2014 - 2024 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.batch2.progress; 021 022import ca.uhn.fhir.batch2.api.IJobCompletionHandler; 023import ca.uhn.fhir.batch2.api.JobCompletionDetails; 024import ca.uhn.fhir.batch2.coordinator.JobDefinitionRegistry; 025import ca.uhn.fhir.batch2.model.JobDefinition; 026import ca.uhn.fhir.batch2.model.JobInstance; 027import ca.uhn.fhir.batch2.model.StatusEnum; 028import ca.uhn.fhir.model.api.IModelJson; 029import ca.uhn.fhir.util.Logs; 030import org.slf4j.Logger; 031 032public class JobInstanceStatusUpdater { 033 private static final Logger ourLog = Logs.getBatchTroubleshootingLog(); 034 private final JobDefinitionRegistry myJobDefinitionRegistry; 035 036 public JobInstanceStatusUpdater(JobDefinitionRegistry theJobDefinitionRegistry) { 037 myJobDefinitionRegistry = theJobDefinitionRegistry; 038 } 039 040 /** 041 * Update the status on the instance, and call any completion handlers when entering a completion state. 042 * @param theJobInstance the instance to mutate 043 * @param theNewStatus target status 044 * @return was the state change allowed? 045 */ 046 public boolean updateInstanceStatus(JobInstance theJobInstance, StatusEnum theNewStatus) { 047 StatusEnum origStatus = theJobInstance.getStatus(); 048 if (origStatus == theNewStatus) { 049 return false; 050 } 051 if (!StatusEnum.isLegalStateTransition(origStatus, theNewStatus)) { 052 ourLog.error( 053 "Ignoring illegal state transition for job instance {} of type {} from {} to {}", 054 theJobInstance.getInstanceId(), 055 theJobInstance.getJobDefinitionId(), 056 origStatus, 057 theNewStatus); 058 return false; 059 } 060 theJobInstance.setStatus(theNewStatus); 061 ourLog.debug( 062 "Updating job instance {} of type {} from {} to {}", 063 theJobInstance.getInstanceId(), 064 theJobInstance.getJobDefinitionId(), 065 origStatus, 066 theNewStatus); 067 handleStatusChange(theJobInstance); 068 069 return true; 070 } 071 072 private <PT extends IModelJson> void handleStatusChange(JobInstance theJobInstance) { 073 JobDefinition<PT> definition = myJobDefinitionRegistry.getJobDefinitionOrThrowException(theJobInstance); 074 assert definition != null; 075 076 switch (theJobInstance.getStatus()) { 077 case COMPLETED: 078 invokeCompletionHandler(theJobInstance, definition, definition.getCompletionHandler()); 079 break; 080 case FAILED: 081 case CANCELLED: 082 invokeCompletionHandler(theJobInstance, definition, definition.getErrorHandler()); 083 break; 084 case QUEUED: 085 case ERRORED: 086 case IN_PROGRESS: 087 case FINALIZE: 088 default: 089 // do nothing 090 } 091 } 092 093 private <PT extends IModelJson> void invokeCompletionHandler( 094 JobInstance theJobInstance, 095 JobDefinition<PT> theJobDefinition, 096 IJobCompletionHandler<PT> theJobCompletionHandler) { 097 if (theJobCompletionHandler == null) { 098 return; 099 } 100 PT jobParameters = theJobInstance.getParameters(theJobDefinition.getParametersType()); 101 JobCompletionDetails<PT> completionDetails = new JobCompletionDetails<>(jobParameters, theJobInstance); 102 theJobCompletionHandler.jobComplete(completionDetails); 103 } 104}