From f7a2ad021ef5add31601831b153d1ad932b72c90 Mon Sep 17 00:00:00 2001 From: mposolda Date: Wed, 22 Jun 2016 17:05:29 +0200 Subject: [PATCH] KEYCLOAK-3141 Fix DB2 and some other DB issues --- .../lock/LiquibaseDBLockProvider.java | 44 +++++---- .../META-INF/db2-jpa-changelog-master.xml | 2 + .../resources/KeycloakApplication.java | 99 +++++++++++-------- .../src/test/resources/log4j.properties | 2 +- 4 files changed, 89 insertions(+), 58 deletions(-) diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/LiquibaseDBLockProvider.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/LiquibaseDBLockProvider.java index 5874084fda..7c48499798 100644 --- a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/LiquibaseDBLockProvider.java +++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/lock/LiquibaseDBLockProvider.java @@ -48,32 +48,35 @@ public class LiquibaseDBLockProvider implements DBLockProvider { private CustomLockService lockService; private Connection dbConnection; + private boolean initialized = false; private int maxAttempts = DEFAULT_MAX_ATTEMPTS; public LiquibaseDBLockProvider(LiquibaseDBLockProviderFactory factory, KeycloakSession session) { this.factory = factory; this.session = session; - init(); } - private void init() { - LiquibaseConnectionProvider liquibaseProvider = session.getProvider(LiquibaseConnectionProvider.class); - JpaConnectionProviderFactory jpaProviderFactory = (JpaConnectionProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(JpaConnectionProvider.class); + private void lazyInit() { + if (!initialized) { + LiquibaseConnectionProvider liquibaseProvider = session.getProvider(LiquibaseConnectionProvider.class); + JpaConnectionProviderFactory jpaProviderFactory = (JpaConnectionProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(JpaConnectionProvider.class); - this.dbConnection = jpaProviderFactory.getConnection(); - String defaultSchema = jpaProviderFactory.getSchema(); + this.dbConnection = jpaProviderFactory.getConnection(); + String defaultSchema = jpaProviderFactory.getSchema(); - try { - Liquibase liquibase = liquibaseProvider.getLiquibase(dbConnection, defaultSchema); + try { + Liquibase liquibase = liquibaseProvider.getLiquibase(dbConnection, defaultSchema); - this.lockService = new CustomLockService(); - lockService.setChangeLogLockWaitTime(factory.getLockWaitTimeoutMillis()); - lockService.setDatabase(liquibase.getDatabase()); - } catch (LiquibaseException exception) { - safeRollbackConnection(); - safeCloseConnection(); - throw new IllegalStateException(exception); + this.lockService = new CustomLockService(); + lockService.setChangeLogLockWaitTime(factory.getLockWaitTimeoutMillis()); + lockService.setDatabase(liquibase.getDatabase()); + initialized = true; + } catch (LiquibaseException exception) { + safeRollbackConnection(); + safeCloseConnection(); + throw new IllegalStateException(exception); + } } } @@ -82,12 +85,15 @@ public class LiquibaseDBLockProvider implements DBLockProvider { safeCloseConnection(); this.dbConnection = null; this.lockService = null; - init(); + initialized = false; + lazyInit(); } @Override public void waitForLock() { + lazyInit(); + while (maxAttempts > 0) { try { lockService.waitForLock(); @@ -110,6 +116,8 @@ public class LiquibaseDBLockProvider implements DBLockProvider { @Override public void releaseLock() { + lazyInit(); + lockService.releaseLock(); lockService.reset(); factory.setHasLock(false); @@ -128,6 +136,8 @@ public class LiquibaseDBLockProvider implements DBLockProvider { @Override public void destroyLockInfo() { + lazyInit(); + try { this.lockService.destroy(); dbConnection.commit(); @@ -154,7 +164,7 @@ public class LiquibaseDBLockProvider implements DBLockProvider { } private void safeCloseConnection() { - // Close after creating EntityManagerFactory to prevent in-mem databases from closing + // Close to prevent in-mem databases from closing if (dbConnection != null) { try { dbConnection.close(); diff --git a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml b/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml index 2d0117efc6..6b9ab90282 100644 --- a/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml +++ b/model/jpa/src/main/resources/META-INF/db2-jpa-changelog-master.xml @@ -32,4 +32,6 @@ + + diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java index 818d571b26..038528156e 100644 --- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java +++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java @@ -27,6 +27,7 @@ import org.keycloak.migration.MigrationModelManager; import org.keycloak.models.*; import org.keycloak.models.dblock.DBLockProvider; import org.keycloak.models.dblock.DBLockManager; +import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.PostMigrationEvent; import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.RealmRepresentation; @@ -91,53 +92,28 @@ public class KeycloakApplication extends Application { singletons.add(new ObjectMapperResolver(Boolean.parseBoolean(System.getProperty("keycloak.jsonPrettyPrint", "false")))); - ExportImportManager exportImportManager; + ExportImportManager[] exportImportManager = new ExportImportManager[1]; - DBLockManager dbLockManager = new DBLockManager(sessionFactory.create()); - dbLockManager.checkForcedUnlock(); - DBLockProvider dbLock = dbLockManager.getDBLock(); - dbLock.waitForLock(); - try { - migrateModel(); + KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() { - KeycloakSession session = sessionFactory.create(); - try { - session.getTransaction().begin(); - - ApplianceBootstrap applianceBootstrap = new ApplianceBootstrap(session); - exportImportManager = new ExportImportManager(session); - - boolean createMasterRealm = applianceBootstrap.isNewInstall(); - if (exportImportManager.isRunImport() && exportImportManager.isImportMasterIncluded()) { - createMasterRealm = false; + @Override + public void run(KeycloakSession lockSession) { + DBLockManager dbLockManager = new DBLockManager(lockSession); + dbLockManager.checkForcedUnlock(); + DBLockProvider dbLock = dbLockManager.getDBLock(); + dbLock.waitForLock(); + try { + exportImportManager[0] = migrateAndBootstrap(); + } finally { + dbLock.releaseLock(); } - - if (createMasterRealm) { - applianceBootstrap.createMasterRealm(contextPath); - } - session.getTransaction().commit(); - } catch (RuntimeException re) { - if (session.getTransaction().isActive()) { - session.getTransaction().rollback(); - } - throw re; - } finally { - session.close(); } - if (exportImportManager.isRunImport()) { - exportImportManager.runImport(); - } else { - importRealms(); - } + }); - importAddUser(); - } finally { - dbLock.releaseLock(); - } - if (exportImportManager.isRunExport()) { - exportImportManager.runExport(); + if (exportImportManager[0].isRunExport()) { + exportImportManager[0].runExport(); } boolean bootstrapAdminUser = false; @@ -158,6 +134,49 @@ public class KeycloakApplication extends Application { setupScheduledTasks(sessionFactory); } + + // Migrate model, bootstrap master realm, import realms and create admin user. This is done with acquired dbLock + protected ExportImportManager migrateAndBootstrap() { + ExportImportManager exportImportManager; + migrateModel(); + + KeycloakSession session = sessionFactory.create(); + try { + session.getTransaction().begin(); + + ApplianceBootstrap applianceBootstrap = new ApplianceBootstrap(session); + exportImportManager = new ExportImportManager(session); + + boolean createMasterRealm = applianceBootstrap.isNewInstall(); + if (exportImportManager.isRunImport() && exportImportManager.isImportMasterIncluded()) { + createMasterRealm = false; + } + + if (createMasterRealm) { + applianceBootstrap.createMasterRealm(contextPath); + } + session.getTransaction().commit(); + } catch (RuntimeException re) { + if (session.getTransaction().isActive()) { + session.getTransaction().rollback(); + } + throw re; + } finally { + session.close(); + } + + if (exportImportManager.isRunImport()) { + exportImportManager.runImport(); + } else { + importRealms(); + } + + importAddUser(); + + return exportImportManager; + } + + protected void migrateModel() { KeycloakSession session = sessionFactory.create(); try { diff --git a/testsuite/integration/src/test/resources/log4j.properties b/testsuite/integration/src/test/resources/log4j.properties index f09369200c..c2bc22c784 100755 --- a/testsuite/integration/src/test/resources/log4j.properties +++ b/testsuite/integration/src/test/resources/log4j.properties @@ -49,7 +49,7 @@ log4j.logger.org.keycloak.connections.jpa.updater.liquibase=${keycloak.liquibase # Enable to view database updates # log4j.logger.org.keycloak.connections.mongo.updater.DefaultMongoUpdaterProvider=debug -# log4j.logger.org.keycloak.connections.jpa.DefaultJpaConnectionProviderFactory=debug +log4j.logger.org.keycloak.connections.jpa.DefaultJpaConnectionProviderFactory=${keycloak.liquibase.logging.level} # log4j.logger.org.keycloak.migration.MigrationModelManager=debug # Enable to view hibernate statistics