Skip to content

Commit 485c431

Browse files
IamMujuziMosesrkorytkowski
authored andcommitted
TRUNK-6418: Run liquibase checks and data imports only when version of core or modules changes
(cherry picked from commit 4723e71c3f39a6467ab6f4060dab42a694cd1a68)
1 parent f737530 commit 485c431

9 files changed

Lines changed: 291 additions & 5 deletions

File tree

api/src/main/java/org/openmrs/api/AdministrationService.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717

1818
import org.openmrs.GlobalProperty;
1919
import org.openmrs.ImplementationId;
20+
import org.openmrs.module.Module;
2021
import org.openmrs.OpenmrsObject;
2122
import org.openmrs.User;
2223
import org.openmrs.annotation.Authorized;
2324
import org.openmrs.api.db.AdministrationDAO;
25+
import org.openmrs.util.DatabaseUpdateException;
2426
import org.openmrs.util.HttpClient;
2527
import org.openmrs.util.OpenmrsConstants;
2628
import org.openmrs.util.PrivilegeConstants;
@@ -420,4 +422,33 @@ public interface AdministrationService extends OpenmrsService {
420422
* <strong>Should</strong> return default common classes if no GPs defined
421423
*/
422424
List<String> getSerializerWhitelistTypes();
425+
426+
/**
427+
* Checks whether a core setup needs to be run due to a version change.
428+
*
429+
* @return true if core setup should be executed because of a version change, false otherwise
430+
*/
431+
public boolean isCoreSetupOnVersionChangeNeeded();
432+
433+
/**
434+
* Checks whether a module setup needs to be run due to a version change.
435+
*
436+
* @param moduleId the identifier of the module to check
437+
* @return true if the module setup should be executed because of a version change, false otherwise
438+
*/
439+
public boolean isModuleSetupOnVersionChangeNeeded(String moduleId);
440+
441+
/**
442+
* Executes the core setup procedures required after a core version change.
443+
*
444+
* @throws DatabaseUpdateException
445+
*/
446+
public void runCoreSetupOnVersionChange() throws DatabaseUpdateException;
447+
448+
/**
449+
* Executes the setup procedures required for a module after a module version change.
450+
*
451+
* @param module the module for which the setup should be executed
452+
*/
453+
public void runModuleSetupOnVersionChange(Module module);
423454
}

api/src/main/java/org/openmrs/api/context/Context.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,13 +1306,18 @@ private static void checkForDatabaseUpdates(Properties props) throws DatabaseUpd
13061306

13071307
// this must be the first thing run in case it changes database mappings
13081308
if (updatesRequired) {
1309-
if (DatabaseUpdater.allowAutoUpdate()) {
1310-
DatabaseUpdater.executeChangelog();
1311-
} else {
1309+
if (!DatabaseUpdater.allowAutoUpdate()) {
13121310
throw new DatabaseUpdateException(
13131311
"Database updates are required. Call Context.updateDatabase() before .startup() to continue.");
13141312
}
13151313
}
1314+
1315+
if (getAdministrationService().isCoreSetupOnVersionChangeNeeded()) {
1316+
log.info("Detected core version change. Running core setup hooks and Liquibase.");
1317+
getAdministrationService().runCoreSetupOnVersionChange();
1318+
}
1319+
1320+
log.info("Database update check completed.");
13161321
}
13171322

13181323
/**

api/src/main/java/org/openmrs/api/impl/AdministrationServiceImpl.java

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
import org.openmrs.module.ModuleUtil;
5959
import org.openmrs.obs.ComplexData;
6060
import org.openmrs.person.PersonMergeLogData;
61+
import org.openmrs.util.DatabaseUpdateException;
62+
import org.openmrs.util.DatabaseUpdater;
6163
import org.openmrs.util.HttpClient;
6264
import org.openmrs.util.LocaleUtility;
6365
import org.openmrs.util.OpenmrsConstants;
@@ -982,4 +984,123 @@ public static List<Class<?>> getSerializerDefaultWhitelistHierarchyTypes() {
982984
PersonMergeLogData.class);
983985
return types;
984986
}
987+
988+
@Override
989+
@SuppressWarnings("unchecked")
990+
public <T> T getRefByUuid(Class<T> type, String uuid) {
991+
if (GlobalProperty.class.equals(type)) {
992+
return (T) getGlobalPropertyByUuid(uuid);
993+
}
994+
throw new APIException("Unsupported type for getRefByUuid: " + type != null ? type.getName() : "null");
995+
}
996+
997+
@Override
998+
public List<Class<?>> getRefTypes() {
999+
return Arrays.asList(GlobalProperty.class);
1000+
}
1001+
1002+
/**
1003+
* @see org.openmrs.api.AdministrationService#isCoreSetupOnVersionChangeNeeded()
1004+
*/
1005+
@Override
1006+
public boolean isCoreSetupOnVersionChangeNeeded() {
1007+
String stored = getStoredCoreVersion();
1008+
String current = OpenmrsConstants.OPENMRS_VERSION_SHORT;
1009+
boolean forceSetup = Boolean.parseBoolean(getGlobalProperty("force.setup", "false"));
1010+
1011+
return forceSetup || !Objects.equals(stored, current);
1012+
}
1013+
1014+
/**
1015+
* @see org.openmrs.api.AdministrationService#isModuleSetupOnVersionChangeNeeded(String)
1016+
*/
1017+
@Override
1018+
public boolean isModuleSetupOnVersionChangeNeeded(String moduleId) {
1019+
String stored = getStoredModuleVersion(moduleId);
1020+
Module module = ModuleFactory.getModuleById(moduleId);
1021+
if (module == null) {
1022+
return false;
1023+
}
1024+
String current = module.getVersion();
1025+
boolean forceSetup = Boolean.parseBoolean(getGlobalProperty("force.setup", "false"));
1026+
1027+
return forceSetup || !Objects.equals(stored, current);
1028+
}
1029+
1030+
/**
1031+
* @see org.openmrs.api.AdministrationService#runCoreSetupOnVersionChange()
1032+
*/
1033+
@Override
1034+
@Transactional
1035+
public void runCoreSetupOnVersionChange() throws DatabaseUpdateException {
1036+
if (!ModuleFactory.getLoadedModules().isEmpty()) {
1037+
String prevCoreVersion = getStoredCoreVersion() != null ? getStoredCoreVersion() : OpenmrsConstants.OPENMRS_VERSION_SHORT;
1038+
1039+
for (Module module : ModuleFactory.getLoadedModules()) {
1040+
String prevModuleVersion = getStoredModuleVersion(module.getModuleId());
1041+
1042+
module.getModuleActivator().setupOnVersionChangeBeforeSchemaChanges(prevCoreVersion, prevModuleVersion);
1043+
}
1044+
}
1045+
1046+
DatabaseUpdater.executeChangelog();
1047+
storeCoreVersion();
1048+
}
1049+
1050+
/**
1051+
* @see org.openmrs.api.AdministrationService#runModuleSetupOnVersionChange(Module)
1052+
*/
1053+
@Override
1054+
@Transactional
1055+
public void runModuleSetupOnVersionChange(Module module) {
1056+
if (module == null) {
1057+
return;
1058+
}
1059+
1060+
String moduleId = module.getModuleId();
1061+
String prevCoreVersion = getStoredCoreVersion() != null ? getStoredCoreVersion() : OpenmrsConstants.OPENMRS_VERSION_SHORT;
1062+
String prevModuleVersion = getStoredModuleVersion(moduleId);
1063+
1064+
ModuleFactory.runLiquibaseForModule(module);
1065+
module.getModuleActivator().setupOnVersionChange(prevCoreVersion, prevModuleVersion);
1066+
1067+
storeModuleVersion(moduleId, module.getVersion());
1068+
}
1069+
1070+
protected String getStoredCoreVersion() {
1071+
return getGlobalProperty("core.version");
1072+
}
1073+
1074+
protected String getStoredModuleVersion(String moduleId) {
1075+
return getGlobalProperty("module." + moduleId + ".version");
1076+
}
1077+
1078+
protected void storeCoreVersion() {
1079+
saveGlobalProperty("core.version", OpenmrsConstants.OPENMRS_VERSION_SHORT, "Saved the state of this core version for future restarts");
1080+
}
1081+
1082+
protected void storeModuleVersion(String moduleId, String version) {
1083+
String propertyName = "module." + moduleId + ".version";
1084+
saveGlobalProperty(propertyName, version, "Saved the state of this module version for future restarts");
1085+
}
1086+
1087+
/**
1088+
* Convenience method to save a global property with the given value. Proxy privileges are added so
1089+
* that this can occur at startup.
1090+
*/
1091+
protected void saveGlobalProperty(String key, String value, String desc) {
1092+
try {
1093+
GlobalProperty gp = getGlobalPropertyObject(key);
1094+
if (gp == null) {
1095+
gp = new GlobalProperty(key, value, desc);
1096+
} else {
1097+
gp.setPropertyValue(value);
1098+
}
1099+
1100+
saveGlobalProperty(gp);
1101+
}
1102+
catch (Exception e) {
1103+
log.warn("Unable to save the global property", e);
1104+
}
1105+
}
9851106
}

api/src/main/java/org/openmrs/module/BaseModuleActivator.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,17 @@ public void willStart() {
6161
public void willStop() {
6262
}
6363

64+
/**
65+
* @see org.openmrs.module.ModuleActivator#setupOnVersionChangeBeforeSchemaChanges(String, String)
66+
*/
67+
@Override
68+
public void setupOnVersionChangeBeforeSchemaChanges(String previousCoreVersion, String previousModuleVersion) {
69+
}
70+
71+
/**
72+
* @see org.openmrs.module.ModuleActivator#setupOnVersionChange(String, String)
73+
*/
74+
@Override
75+
public void setupOnVersionChange(String previousCoreVersion, String previousModuleVersion) {
76+
}
6477
}

api/src/main/java/org/openmrs/module/ModuleActivator.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,14 @@ public interface ModuleActivator {
5656
*/
5757
public void stopped();
5858

59+
/**
60+
* Called before Liquibase runs, but only if core or this module version changed.
61+
*/
62+
default void setupOnVersionChangeBeforeSchemaChanges(String previousCoreVersion, String previousModuleVersion) {}
63+
64+
/**
65+
* Called after Liquibase runs, but only if core or this module version changed.
66+
*/
67+
default void setupOnVersionChange(String previousCoreVersion, String previousModuleVersion) {}
68+
5969
}

api/src/main/java/org/openmrs/module/ModuleFactory.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -691,8 +691,10 @@ public static Module startModuleInternal(Module module, boolean isOpenmrsStartup
691691
Context.removeProxyPrivilege("");
692692
}
693693

694-
// run module's optional liquibase.xml immediately after sqldiff.xml
695-
runLiquibase(module);
694+
if (Context.getAdministrationService().isModuleSetupOnVersionChangeNeeded(module.getModuleId())) {
695+
log.info("Detected version change for module {}. Running setup hooks and module Liquibase.", module.getModuleId());
696+
Context.getAdministrationService().runModuleSetupOnVersionChange(module);
697+
}
696698

697699
// effectively mark this module as started successfully
698700
getStartedModulesMap().put(moduleId, module);
@@ -939,6 +941,13 @@ private static void runDiff(Module module, String version, String sql) {
939941
}
940942

941943
}
944+
945+
/**
946+
* This is a convenience method that exposes the private {@link #runLiquibase(Module)} method.
947+
*/
948+
public static void runLiquibaseForModule(Module module) {
949+
runLiquibase(module);
950+
}
942951

943952
/**
944953
* Execute all not run changeSets in liquibase.xml for the given module

api/src/test/java/org/openmrs/api/AdministrationServiceTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import static org.junit.jupiter.api.Assertions.assertThrows;
2828
import static org.junit.jupiter.api.Assertions.assertTrue;
2929
import static org.mockito.Mockito.mock;
30+
import static org.mockito.Mockito.verify;
3031

3132
import java.util.ArrayList;
3233
import java.util.Arrays;
@@ -51,6 +52,8 @@
5152
import org.openmrs.customdatatype.datatype.DateDatatype;
5253
import org.openmrs.messagesource.MutableMessageSource;
5354
import org.openmrs.messagesource.impl.MutableResourceBundleMessageSource;
55+
import org.openmrs.module.Module;
56+
import org.openmrs.module.ModuleActivator;
5457
import org.openmrs.test.jupiter.BaseContextSensitiveTest;
5558
import org.openmrs.util.HttpClient;
5659
import org.openmrs.util.LocaleUtility;
@@ -1160,4 +1163,28 @@ public void getSerializerWhitelistTypes_shouldReturnDefaultCommonClassesIfNoGPS(
11601163
"hierarchyOf:org.openmrs.messagesource.PresentationMessage",
11611164
"hierarchyOf:org.openmrs.person.PersonMergeLogData"));
11621165
}
1166+
1167+
@Test
1168+
public void runModuleSetupOnVersionChange_shouldExecuteLiquibaseAndStoreNewVersion() {
1169+
// old version
1170+
adminService.setGlobalProperty("module.testmodule.version", "1.0.0");
1171+
assertEquals("1.0.0", adminService.getGlobalProperty("module.testmodule.version"));
1172+
1173+
String previousModuleVersion = "1.0.0";
1174+
String previousCoreVersion = OpenmrsConstants.OPENMRS_VERSION_SHORT;
1175+
1176+
Module module = new Module("Test Module");
1177+
module.setModuleId("testmodule");
1178+
module.setVersion("1.2.3");
1179+
1180+
ModuleActivator activator = mock(ModuleActivator.class);
1181+
module.setModuleActivator(activator);
1182+
1183+
adminService.runModuleSetupOnVersionChange(module);
1184+
1185+
assertEquals("1.2.3", adminService.getGlobalProperty("module.testmodule.version"));
1186+
1187+
// verify hook methods must be called
1188+
verify(activator).setupOnVersionChange(previousCoreVersion, previousModuleVersion);
1189+
}
11631190
}

0 commit comments

Comments
 (0)