001package ca.uhn.fhir.jpa.migrate; 002 003/*- 004 * #%L 005 * HAPI FHIR Server - SQL Migration 006 * %% 007 * Copyright (C) 2014 - 2023 Smile CDR, Inc. 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023import ca.uhn.fhir.i18n.Msg; 024import ca.uhn.fhir.jpa.migrate.dao.HapiMigrationDao; 025import ca.uhn.fhir.jpa.migrate.entity.HapiMigrationEntity; 026import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask; 027import org.flywaydb.core.api.MigrationVersion; 028 029import java.util.Optional; 030import java.util.Set; 031 032public class HapiMigrationStorageSvc { 033 public static final String UNKNOWN_VERSION = "unknown"; 034 public static final String LOCK_TYPE = "hapi-fhir-lock"; 035 036 private final HapiMigrationDao myHapiMigrationDao; 037 038 public HapiMigrationStorageSvc(HapiMigrationDao theHapiMigrationDao) { 039 myHapiMigrationDao = theHapiMigrationDao; 040 } 041 042 public String getMigrationTablename() { 043 return myHapiMigrationDao.getMigrationTablename(); 044 } 045 046 /** 047 * Returns a list of migration tasks that have not yet been successfully run against the database 048 * @param theTaskList the full list of tasks for this release 049 * @return a list of tasks that have not yet been successfully run against the database 050 */ 051 052 public MigrationTaskList diff(MigrationTaskList theTaskList) { 053 Set<MigrationVersion> appliedMigrationVersions = fetchAppliedMigrationVersions(); 054 055 return theTaskList.diff(appliedMigrationVersions); 056 } 057 058 /** 059 * 060 * @return a list of migration versions that have been successfully run against the database 061 */ 062 Set<MigrationVersion> fetchAppliedMigrationVersions() { 063 return myHapiMigrationDao.fetchSuccessfulMigrationVersions(); 064 } 065 066 /** 067 * 068 * @return the most recent version that was run against the database (used for logging purposes) 069 */ 070 public String getLatestAppliedVersion() { 071 return fetchAppliedMigrationVersions().stream() 072 .sorted() 073 .map(MigrationVersion::toString) 074 .reduce((first, second) -> second) 075 .orElse(UNKNOWN_VERSION); 076 } 077 078 /** 079 * Save a migration task to the database 080 */ 081 public void saveTask(BaseTask theBaseTask, Integer theMillis, boolean theSuccess) { 082 HapiMigrationEntity entity = HapiMigrationEntity.fromBaseTask(theBaseTask); 083 entity.setExecutionTime(theMillis); 084 entity.setSuccess(theSuccess); 085 myHapiMigrationDao.save(entity); 086 } 087 088 /** 089 * Create the migration table if it does not already exist 090 */ 091 092 public void createMigrationTableIfRequired() { 093 myHapiMigrationDao.createMigrationTableIfRequired(); 094 } 095 096 097 /** 098 * 099 * @param theLockDescription value of the Description for the lock record 100 * @return true if the record was successfully deleted 101 */ 102 public boolean deleteLockRecord(String theLockDescription) { 103 verifyNoOtherLocksPresent(theLockDescription); 104 105 // Remove the locking row 106 return myHapiMigrationDao.deleteLockRecord(HapiMigrationLock.LOCK_PID, theLockDescription); 107 } 108 109 void verifyNoOtherLocksPresent(String theLockDescription) { 110 Optional<HapiMigrationEntity> otherLockFound = myHapiMigrationDao.findFirstByPidAndNotDescription(HapiMigrationLock.LOCK_PID, theLockDescription); 111 112 // Check that there are no other locks in place. This should not happen! 113 if (otherLockFound.isPresent()) { 114 throw new HapiMigrationException(Msg.code(2152) + "Internal error: on unlocking, a competing lock was found"); 115 } 116 } 117 118 public boolean insertLockRecord(String theLockDescription) { 119 HapiMigrationEntity entity = new HapiMigrationEntity(); 120 entity.setPid(HapiMigrationLock.LOCK_PID); 121 entity.setType(LOCK_TYPE); 122 entity.setDescription(theLockDescription); 123 entity.setExecutionTime(0); 124 entity.setSuccess(true); 125 126 return myHapiMigrationDao.save(entity); 127 } 128 129 public Optional<HapiMigrationEntity> findFirstByPidAndNotDescription(Integer theLockPid, String theLockDescription) { 130 return myHapiMigrationDao.findFirstByPidAndNotDescription(theLockPid, theLockDescription); 131 } 132}