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}