Skip to content

Commit 76ef78d

Browse files
committed
TRUNK-6469: Add support for automatic initialization of Envers audit tables
1 parent 8cd859a commit 76ef78d

File tree

4 files changed

+411
-1
lines changed

4 files changed

+411
-1
lines changed

api/src/main/java/org/openmrs/api/db/hibernate/HibernateSessionFactoryBean.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.openmrs.api.context.Context;
3333
import org.openmrs.module.Module;
3434
import org.openmrs.module.ModuleFactory;
35+
import org.openmrs.util.EnversAuditTableInitializer;
3536
import org.openmrs.util.OpenmrsUtil;
3637
import org.slf4j.Logger;
3738
import org.slf4j.LoggerFactory;
@@ -221,6 +222,7 @@ public void destroy() throws HibernateException {
221222
public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactory,
222223
SessionFactoryServiceRegistry serviceRegistry) {
223224
this.metadata = metadata;
225+
generateEnversAuditTables(metadata, serviceRegistry);
224226
}
225227

226228
@Override
@@ -234,4 +236,13 @@ public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactor
234236
public Metadata getMetadata() {
235237
return metadata;
236238
}
239+
240+
private void generateEnversAuditTables(Metadata metadata, SessionFactoryServiceRegistry serviceRegistry) {
241+
try {
242+
Properties hibernateProperties = getHibernateProperties();
243+
EnversAuditTableInitializer.initialize(metadata, hibernateProperties, serviceRegistry);
244+
} catch (Exception e) {
245+
log.error("Failed to initialize Envers audit tables", e);
246+
}
247+
}
237248
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/**
2+
* This Source Code Form is subject to the terms of the Mozilla Public License,
3+
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
4+
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
5+
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
6+
*
7+
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
8+
* graphic logo is a trademark of OpenMRS Inc.
9+
*/
10+
package org.openmrs.util;
11+
12+
import java.util.EnumSet;
13+
import java.util.HashMap;
14+
import java.util.Map;
15+
import java.util.Properties;
16+
17+
import org.hibernate.boot.Metadata;
18+
import org.hibernate.service.ServiceRegistry;
19+
import org.hibernate.tool.schema.TargetType;
20+
import org.hibernate.tool.schema.spi.ExceptionHandler;
21+
import org.hibernate.tool.schema.spi.ExecutionOptions;
22+
import org.hibernate.tool.schema.spi.SchemaFilter;
23+
import org.hibernate.tool.schema.spi.SchemaManagementTool;
24+
import org.hibernate.tool.schema.spi.SchemaMigrator;
25+
import org.hibernate.tool.schema.spi.ScriptTargetOutput;
26+
import org.hibernate.tool.schema.spi.TargetDescriptor;
27+
import org.slf4j.Logger;
28+
import org.slf4j.LoggerFactory;
29+
30+
/**
31+
* Initializes Hibernate Envers audit tables when auditing is enabled. This class is responsible for
32+
* conditionally creating audit tables only when hibernate.integration.envers.enabled=true.
33+
*/
34+
public class EnversAuditTableInitializer {
35+
36+
private static final Logger log = LoggerFactory.getLogger(EnversAuditTableInitializer.class);
37+
38+
/**
39+
* Checks if Envers is enabled and creates/updates audit tables as needed. This will Create or
40+
* Update audit tables if they don't exist - Update existing audit tables if the schema has
41+
* changed
42+
*
43+
* @param metadata Hibernate metadata containing entity mappings
44+
* @param hibernateProperties properties containing Envers configuration
45+
* @param serviceRegistry Hibernate service registry
46+
*/
47+
public static void initialize(Metadata metadata, Properties hibernateProperties,
48+
ServiceRegistry serviceRegistry) {
49+
50+
if (!isEnversEnabled(hibernateProperties)) {
51+
log.debug("Hibernate Envers is not enabled. Skipping audit table initialization.");
52+
return;
53+
}
54+
55+
try {
56+
updateAuditTables(metadata, hibernateProperties, serviceRegistry);
57+
log.info("Envers audit table initialization completed successfully.");
58+
}
59+
catch (Exception e) {
60+
log.error("Failed to initialize Envers audit tables. ", e);
61+
throw new RuntimeException(e);
62+
}
63+
}
64+
65+
/**
66+
* Checks if Hibernate Envers is enabled in the configuration.
67+
*
68+
* @param properties Hibernate properties
69+
* @return true if Envers is enabled, false otherwise
70+
*/
71+
private static boolean isEnversEnabled(Properties properties) {
72+
String enversEnabled = properties.getProperty("hibernate.integration.envers.enabled");
73+
return "true".equalsIgnoreCase(enversEnabled);
74+
}
75+
76+
/**
77+
* Creates or updates audit tables using Hibernate's {@link SchemaMigrator}. This method filters
78+
* to only process audit tables.
79+
*
80+
* @param metadata Hibernate metadata containing entity mappings (includes Envers audit
81+
* entities)
82+
* @param hibernateProperties Hibernate configuration properties
83+
* @param serviceRegistry Hibernate service registry
84+
*/
85+
private static void updateAuditTables(Metadata metadata, Properties hibernateProperties,
86+
ServiceRegistry serviceRegistry) throws Exception {
87+
throw new Exception();
88+
// String auditTablePrefix = hibernateProperties.getProperty("org.hibernate.envers.audit_table_prefix", "");
89+
// String auditTableSuffix = hibernateProperties.getProperty("org.hibernate.envers.audit_table_suffix", "_audit");
90+
//
91+
// Map<String, Object> settings = new HashMap<>();
92+
// for (String key : hibernateProperties.stringPropertyNames()) {
93+
// settings.put(key, hibernateProperties.getProperty(key));
94+
// }
95+
//
96+
// ExecutionOptions executionOptions = getExecutionOptions(settings);
97+
//
98+
// SchemaMigrator schemaMigrator = serviceRegistry.getService(SchemaManagementTool.class).getSchemaMigrator(settings);
99+
//
100+
// TargetDescriptor targetDescriptor = getTargetDescriptor();
101+
//
102+
// schemaMigrator.doMigration(metadata, executionOptions, contributed -> {
103+
// String tableName = contributed.getExportIdentifier();
104+
// if (tableName == null) {
105+
// return false;
106+
// }
107+
//
108+
// String lowerTableName = tableName.toLowerCase();
109+
//
110+
// if (lowerTableName.contains("revision") || lowerTableName.equals("revinfo")) {
111+
// return true;
112+
// }
113+
//
114+
// String lowerPrefix = auditTablePrefix.toLowerCase();
115+
// String lowerSuffix = auditTableSuffix.toLowerCase();
116+
//
117+
// boolean hasPrefix = lowerPrefix.isEmpty() || lowerTableName.startsWith(lowerPrefix);
118+
// boolean hasSuffix = lowerSuffix.isEmpty() || lowerTableName.endsWith(lowerSuffix);
119+
//
120+
// return hasPrefix && hasSuffix;
121+
// }, targetDescriptor);
122+
//
123+
// log.info("Successfully created/updated Envers audit tables using Hibernate SchemaManagementTool.");
124+
}
125+
126+
private static TargetDescriptor getTargetDescriptor() {
127+
return new TargetDescriptor() {
128+
@Override
129+
public EnumSet<TargetType> getTargetTypes() {
130+
return EnumSet.of(TargetType.DATABASE);
131+
}
132+
133+
@Override
134+
public ScriptTargetOutput getScriptTargetOutput() {
135+
return null;
136+
}
137+
};
138+
}
139+
140+
private static ExecutionOptions getExecutionOptions(Map<String, Object> settings) {
141+
return new ExecutionOptions() {
142+
@Override
143+
public Map<String, Object> getConfigurationValues() {
144+
return settings;
145+
}
146+
147+
@Override
148+
public boolean shouldManageNamespaces() {
149+
return false;
150+
}
151+
152+
@Override
153+
public ExceptionHandler getExceptionHandler() {
154+
return throwable -> log.warn("Schema migration encountered an issue: {}", throwable.getMessage());
155+
}
156+
157+
@Override
158+
public SchemaFilter getSchemaFilter() {
159+
return SchemaFilter.ALL;
160+
}
161+
};
162+
}
163+
}

api/src/test/java/org/openmrs/util/DatabaseIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public class DatabaseIT implements LiquibaseProvider {
4444
protected static final String PASSWORD = "test";
4545

4646
@BeforeEach
47-
public void setup() throws SQLException, ClassNotFoundException {
47+
public void setup() throws Exception {
4848
this.initializeDatabase();
4949
}
5050

0 commit comments

Comments
 (0)