keycloak-aplcache

Changes

connections/mongo/src/main/java/org/keycloak/connections/mongo/api/MongoIndex.java 26(+0 -26)

connections/mongo/src/main/java/org/keycloak/connections/mongo/api/MongoIndexes.java 22(+0 -22)

pom.xml 6(+6 -0)

Details

diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
index 4bd1d6e..5a00c74 100755
--- a/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
+++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java
@@ -1,12 +1,20 @@
 package org.keycloak.connections.jpa;
 
 import org.hibernate.ejb.AvailableSettings;
+import org.jboss.logging.Logger;
 import org.keycloak.Config;
+import org.keycloak.connections.jpa.updater.JpaUpdaterProvider;
 import org.keycloak.models.KeycloakSession;
 
+import javax.naming.InitialContext;
 import javax.persistence.EntityManager;
 import javax.persistence.EntityManagerFactory;
 import javax.persistence.Persistence;
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -15,13 +23,15 @@ import java.util.Map;
  */
 public class DefaultJpaConnectionProviderFactory implements JpaConnectionProviderFactory {
 
+    private static final Logger logger = Logger.getLogger(DefaultJpaConnectionProviderFactory.class);
+
     private volatile EntityManagerFactory emf;
 
     private Config.Scope config;
 
     @Override
     public JpaConnectionProvider create(KeycloakSession session) {
-        lazyInit();
+        lazyInit(session);
 
         EntityManager em = emf.createEntityManager();
         em = PersistenceExceptionConverter.create(em);
@@ -46,11 +56,17 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
         this.config = config;
     }
 
-    private void lazyInit() {
+    private void lazyInit(KeycloakSession session) {
         if (emf == null) {
             synchronized (this) {
                 if (emf == null) {
+                    logger.debug("Initializing JPA connections");
+
+                    Connection connection = null;
+
                     String unitName = config.get("unitName");
+                    String databaseSchema = config.get("databaseSchema");
+
                     Map<String, Object> properties = new HashMap<String, Object>();
 
                     // Only load config from keycloak-server.json if unitName is not specified
@@ -83,19 +99,80 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide
                             properties.put("hibernate.dialect", driverDialect);
                         }
 
-                        String databaseSchema = config.get("databaseSchema", "validate");
                         if (databaseSchema != null) {
-                            properties.put("hibernate.hbm2ddl.auto", databaseSchema);
+                            if (databaseSchema.equals("development-update")) {
+                                properties.put("hibernate.hbm2ddl.auto", "update");
+                                databaseSchema = null;
+                            } else if (databaseSchema.equals("development-validate")) {
+                                properties.put("hibernate.hbm2ddl.auto", "validate");
+                                databaseSchema = null;
+                            }
                         }
 
                         properties.put("hibernate.show_sql", config.getBoolean("showSql", false));
                         properties.put("hibernate.format_sql", config.getBoolean("formatSql", true));
                     }
 
+                    if (databaseSchema != null) {
+                        logger.trace("Updating database");
+
+                        JpaUpdaterProvider updater = session.getProvider(JpaUpdaterProvider.class);
+                        connection = getConnection();
+
+                        if (databaseSchema.equals("update")) {
+                            String currentVersion = null;
+                            try {
+                                ResultSet resultSet = connection.createStatement().executeQuery(updater.getCurrentVersionSql());
+                                if (resultSet.next()) {
+                                    currentVersion = resultSet.getString(1);
+                                }
+                            } catch (SQLException e) {
+                            }
+
+                            if (currentVersion == null || !JpaUpdaterProvider.LAST_VERSION.equals(currentVersion)) {
+                                updater.update(connection);
+                            } else {
+                                logger.debug("Database is up to date");
+                            }
+                        } else if (databaseSchema.equals("validate")) {
+                            updater.validate(connection);
+                        } else {
+                            throw new RuntimeException("Invalid value for databaseSchema: " + databaseSchema);
+                        }
+
+                        logger.trace("Database update completed");
+                    }
+
+                    logger.trace("Creating EntityManagerFactory");
                     emf = Persistence.createEntityManagerFactory(unitName, properties);
+                    logger.trace("EntityManagerFactory created");
+
+                    // Close after creating EntityManagerFactory to prevent in-mem databases from closing
+                    if (connection != null) {
+                        try {
+                            connection.close();
+                        } catch (SQLException e) {
+                            logger.warn(e);
+                        }
+                    }
                 }
             }
         }
     }
 
+    private Connection getConnection() {
+        try {
+            String dataSourceLookup = config.get("dataSource");
+            if (dataSourceLookup != null) {
+                DataSource dataSource = (DataSource) new InitialContext().lookup(dataSourceLookup);
+                return dataSource.getConnection();
+            } else {
+                Class.forName(config.get("driver"));
+                return DriverManager.getConnection(config.get("url"), config.get("user"), config.get("password"));
+            }
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to connect to database", e);
+        }
+    }
+
 }
diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java
new file mode 100644
index 0000000..9e19f7f
--- /dev/null
+++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java
@@ -0,0 +1,22 @@
+package org.keycloak.connections.jpa.updater;
+
+import org.keycloak.provider.Provider;
+
+import java.sql.Connection;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface JpaUpdaterProvider extends Provider {
+
+    public String FIRST_VERSION = "1.0.0.Final";
+
+    public String LAST_VERSION = "1.1.0.Beta1";
+
+    public String getCurrentVersionSql();
+
+    public void update(Connection connection);
+
+    public void validate(Connection connection);
+
+}
diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProviderFactory.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProviderFactory.java
new file mode 100644
index 0000000..29a89f4
--- /dev/null
+++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProviderFactory.java
@@ -0,0 +1,9 @@
+package org.keycloak.connections.jpa.updater;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface JpaUpdaterProviderFactory extends ProviderFactory<JpaUpdaterProvider> {
+}
diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterSpi.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterSpi.java
new file mode 100644
index 0000000..e95242a
--- /dev/null
+++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterSpi.java
@@ -0,0 +1,27 @@
+package org.keycloak.connections.jpa.updater;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class JpaUpdaterSpi implements Spi {
+
+    @Override
+    public String getName() {
+        return "connectionsJpaUpdater";
+    }
+
+    @Override
+    public Class<? extends Provider> getProviderClass() {
+        return JpaUpdaterProvider.class;
+    }
+
+    @Override
+    public Class<? extends ProviderFactory> getProviderFactoryClass() {
+        return JpaUpdaterProviderFactory.class;
+    }
+
+}
diff --git a/connections/jpa/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/connections/jpa/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index d7b47a8..175bcfb 100644
--- a/connections/jpa/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/connections/jpa/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -1 +1,2 @@
-org.keycloak.connections.jpa.JpaConnectionSpi
\ No newline at end of file
+org.keycloak.connections.jpa.JpaConnectionSpi
+org.keycloak.connections.jpa.updater.JpaUpdaterSpi
\ No newline at end of file
diff --git a/connections/jpa-liquibase/pom.xml b/connections/jpa-liquibase/pom.xml
new file mode 100755
index 0000000..5196c42
--- /dev/null
+++ b/connections/jpa-liquibase/pom.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <parent>
+        <artifactId>keycloak-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.1.0-Alpha1-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>keycloak-connections-jpa-liquibase</artifactId>
+    <name>Keycloak Connections JPA Liquibase Updater</name>
+    <description/>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-connections-jpa</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.liquibase</groupId>
+            <artifactId>liquibase-core</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.yaml</groupId>
+                    <artifactId>snakeyaml</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.jboss.logging</groupId>
+            <artifactId>jboss-logging</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.liquibase</groupId>
+                <artifactId>liquibase-maven-plugin</artifactId>
+<!-- set to ${liquibase.version} once Liquibase 3.2.3 is released (https://liquibase.jira.com/browse/CORE-1987) -->	
+                <version>3.1.1</version>
+                <configuration>
+                    <changeLogFile>META-INF/jpa-changelog-master.xml</changeLogFile>
+
+                    <url>${url}</url>
+                    <driver>${driver}</driver>
+                    <username>${username}</username>
+                    <password>${password}</password>
+
+                    <referenceUrl>${referenceUrl}</referenceUrl>
+                    <referenceDriver>${driver}</referenceDriver>
+                    <referenceUsername>${username}</referenceUsername>
+                    <referencePassword>${password}</referencePassword>
+                </configuration>
+                <dependencies>
+                    <dependency>
+                        <groupId>com.h2database</groupId>
+                        <artifactId>h2</artifactId>
+                        <version>${h2.version}</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+        </plugins>
+    </build>
+
+    <properties>
+        <username>sa</username>
+        <password></password>
+        <driver>org.h2.Driver</driver>
+    </properties>
+</project>
diff --git a/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java
new file mode 100644
index 0000000..8d1f6fa
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProvider.java
@@ -0,0 +1,208 @@
+package org.keycloak.connections.jpa.updater.liquibase;
+
+import liquibase.Contexts;
+import liquibase.Liquibase;
+import liquibase.changelog.ChangeSet;
+import liquibase.changelog.DatabaseChangeLog;
+import liquibase.changelog.RanChangeSet;
+import liquibase.database.Database;
+import liquibase.database.DatabaseFactory;
+import liquibase.database.core.DB2Database;
+import liquibase.database.core.DerbyDatabase;
+import liquibase.database.core.FirebirdDatabase;
+import liquibase.database.core.H2Database;
+import liquibase.database.core.HsqlDatabase;
+import liquibase.database.core.InformixDatabase;
+import liquibase.database.core.MSSQLDatabase;
+import liquibase.database.core.MySQLDatabase;
+import liquibase.database.core.OracleDatabase;
+import liquibase.database.core.PostgresDatabase;
+import liquibase.database.core.SQLiteDatabase;
+import liquibase.database.core.SybaseASADatabase;
+import liquibase.database.core.SybaseDatabase;
+import liquibase.database.jvm.JdbcConnection;
+import liquibase.exception.LiquibaseException;
+import liquibase.logging.LogFactory;
+import liquibase.logging.LogLevel;
+import liquibase.resource.ClassLoaderResourceAccessor;
+import liquibase.servicelocator.ServiceLocator;
+import org.jboss.logging.Logger;
+import org.keycloak.connections.jpa.updater.JpaUpdaterProvider;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
+
+    private static final Logger logger = Logger.getLogger(LiquibaseJpaUpdaterProvider.class);
+
+    private static final String CHANGELOG = "META-INF/jpa-changelog-master.xml";
+
+    @Override
+    public String getCurrentVersionSql() {
+        return "SELECT ID from DATABASECHANGELOG ORDER BY DATEEXECUTED DESC LIMIT 1";
+    }
+
+    @Override
+    public void update(Connection connection) {
+        logger.debug("Starting database update");
+
+        try {
+            Liquibase liquibase = getLiquibase(connection);
+
+            List<ChangeSet> changeSets = liquibase.listUnrunChangeSets((Contexts) null);
+            if (!changeSets.isEmpty()) {
+                if (changeSets.get(0).getId().equals(FIRST_VERSION)) {
+                    Statement statement = connection.createStatement();
+                    try {
+                        statement.executeQuery("SELECT id FROM REALM");
+
+                        logger.infov("Updating database from {0} to {1}", FIRST_VERSION, changeSets.get(changeSets.size() - 1).getId());
+                        liquibase.markNextChangeSetRan((Contexts) null);
+                    } catch (SQLException e) {
+                        logger.info("Initializing database schema");
+                    }
+                } else {
+                    if (logger.isDebugEnabled()) {
+                        List<RanChangeSet> ranChangeSets = liquibase.getDatabase().getRanChangeSetList();
+                        logger.debugv("Updating database from {0} to {1}", ranChangeSets.get(ranChangeSets.size() - 1).getId(), changeSets.get(changeSets.size() - 1).getId());
+                    } else {
+                        logger.infov("Updating database");
+                    }
+                }
+
+                liquibase.update((Contexts) null);
+            }
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to update database", e);
+        }
+        logger.debug("Completed database update");
+    }
+
+    @Override
+    public void validate(Connection connection) {
+        try {
+            Liquibase liquibase = getLiquibase(connection);
+
+            liquibase.validate();
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to validate database", e);
+        }
+    }
+
+    private Liquibase getLiquibase(Connection connection) throws Exception {
+        if (!System.getProperties().containsKey("liquibase.scan.packages")) {
+            System.setProperty("liquibase.scan.packages", "liquibase.change,liquibase.changelog,liquibase.database,liquibase.parser,liquibase.precondition,liquibase.datatype,liquibase.serializer,liquibase.sqlgenerator,liquibase.executor,liquibase.snapshot,liquibase.logging,liquibase.diff,liquibase.structure,liquibase.structurecompare,liquibase.lockservice");
+        }
+        LogFactory.setInstance(new LogWrapper());
+        Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection));
+        return new Liquibase(CHANGELOG, new ClassLoaderResourceAccessor(getClass().getClassLoader()), database);
+    }
+
+    @Override
+    public void close() {
+    }
+
+    private static class LogWrapper extends LogFactory {
+
+        private liquibase.logging.Logger logger = new liquibase.logging.Logger() {
+            @Override
+            public void setName(String name) {
+            }
+
+            @Override
+            public void setLogLevel(String level) {
+            }
+
+            @Override
+            public void setLogLevel(LogLevel level) {
+            }
+
+            @Override
+            public void setLogLevel(String logLevel, String logFile) {
+            }
+
+            @Override
+            public void severe(String message) {
+                LiquibaseJpaUpdaterProvider.logger.error(message);
+            }
+
+            @Override
+            public void severe(String message, Throwable e) {
+                LiquibaseJpaUpdaterProvider.logger.error(message, e);
+            }
+
+            @Override
+            public void warning(String message) {
+                LiquibaseJpaUpdaterProvider.logger.warn(message);
+            }
+
+            @Override
+            public void warning(String message, Throwable e) {
+                LiquibaseJpaUpdaterProvider.logger.warn(message, e);
+            }
+
+            @Override
+            public void info(String message) {
+                LiquibaseJpaUpdaterProvider.logger.debug(message);
+            }
+
+            @Override
+            public void info(String message, Throwable e) {
+                LiquibaseJpaUpdaterProvider.logger.debug(message, e);
+            }
+
+            @Override
+            public void debug(String message) {
+                LiquibaseJpaUpdaterProvider.logger.trace(message);
+            }
+
+            @Override
+            public LogLevel getLogLevel() {
+                if (LiquibaseJpaUpdaterProvider.logger.isTraceEnabled()) {
+                    return LogLevel.DEBUG;
+                } else if (LiquibaseJpaUpdaterProvider.logger.isDebugEnabled()) {
+                    return LogLevel.INFO;
+                } else {
+                    return LogLevel.WARNING;
+                }
+            }
+
+            @Override
+            public void debug(String message, Throwable e) {
+                LiquibaseJpaUpdaterProvider.logger.trace(message, e);
+            }
+
+            @Override
+            public void setChangeLog(DatabaseChangeLog databaseChangeLog) {
+            }
+
+            @Override
+            public void setChangeSet(ChangeSet changeSet) {
+            }
+
+            @Override
+            public int getPriority() {
+                return 0;
+            }
+        };
+
+        @Override
+        public liquibase.logging.Logger getLog(String name) {
+            return logger;
+        }
+
+        @Override
+        public liquibase.logging.Logger getLog() {
+            return logger;
+        }
+
+    }
+
+}
diff --git a/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProviderFactory.java b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProviderFactory.java
new file mode 100644
index 0000000..d88b787
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/java/org/keycloak/connections/jpa/updater/liquibase/LiquibaseJpaUpdaterProviderFactory.java
@@ -0,0 +1,31 @@
+package org.keycloak.connections.jpa.updater.liquibase;
+
+import org.keycloak.Config;
+import org.keycloak.connections.jpa.updater.JpaUpdaterProvider;
+import org.keycloak.connections.jpa.updater.JpaUpdaterProviderFactory;
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class LiquibaseJpaUpdaterProviderFactory implements JpaUpdaterProviderFactory {
+
+    @Override
+    public JpaUpdaterProvider create(KeycloakSession session) {
+        return new LiquibaseJpaUpdaterProvider();
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+    }
+
+    @Override
+    public void close() {
+    }
+
+    @Override
+    public String getId() {
+        return "liquibase";
+    }
+
+}
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.0.0.Final.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.0.0.Final.xml
new file mode 100644
index 0000000..5b5e921
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.0.0.Final.xml
@@ -0,0 +1,376 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.2.xsd">
+    <changeSet author="sthorger@redhat.com" id="1.0.0.Final">
+        <createTable tableName="APPLICATION_DEFAULT_ROLES">
+            <column name="APPLICATION_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="ROLE_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+        <createTable tableName="CLIENT">
+            <column name="DTYPE" type="VARCHAR(31)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="ALLOWED_CLAIMS_MASK" type="BIGINT(19)"/>
+            <column name="ENABLED" type="BOOLEAN(1)"/>
+            <column name="FULL_SCOPE_ALLOWED" type="BOOLEAN(1)"/>
+            <column name="NAME" type="VARCHAR(255)"/>
+            <column name="NOT_BEFORE" type="INT(10)"/>
+            <column name="PUBLIC_CLIENT" type="BOOLEAN(1)"/>
+            <column name="SECRET" type="VARCHAR(255)"/>
+            <column name="BASE_URL" type="VARCHAR(255)"/>
+            <column name="BEARER_ONLY" type="BOOLEAN(1)"/>
+            <column name="MANAGEMENT_URL" type="VARCHAR(255)"/>
+            <column name="SURROGATE_AUTH_REQUIRED" type="BOOLEAN(1)"/>
+            <column name="DIRECT_GRANTS_ONLY" type="BOOLEAN(1)"/>
+            <column name="REALM_ID" type="VARCHAR(36)"/>
+        </createTable>
+        <createTable tableName="CLIENT_SESSION">
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="ACTION" type="INT(10)"/>
+            <column name="CLIENT_ID" type="VARCHAR(36)"/>
+            <column name="REDIRECT_URI" type="VARCHAR(255)"/>
+            <column name="STATE" type="VARCHAR(255)"/>
+            <column name="TIMESTAMP" type="INT(10)"/>
+            <column name="SESSION_ID" type="VARCHAR(36)"/>
+        </createTable>
+        <createTable tableName="CLIENT_SESSION_ROLE">
+            <column name="ROLE_ID" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="CLIENT_SESSION" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+        <createTable tableName="COMPOSITE_ROLE">
+            <column name="COMPOSITE" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="CHILD_ROLE" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+        <createTable tableName="CREDENTIAL">
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="DEVICE" type="VARCHAR(255)"/>
+            <column name="HASH_ITERATIONS" type="INT(10)"/>
+            <column name="SALT" type="BINARY(255)"/>
+            <column name="TYPE" type="VARCHAR(255)"/>
+            <column name="VALUE" type="VARCHAR(255)"/>
+            <column name="USER_ID" type="VARCHAR(36)"/>
+        </createTable>
+        <createTable tableName="EVENT_ENTITY">
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="CLIENT_ID" type="VARCHAR(255)"/>
+            <column name="DETAILS_JSON" type="VARCHAR(2550)"/>
+            <column name="ERROR" type="VARCHAR(255)"/>
+            <column name="IP_ADDRESS" type="VARCHAR(255)"/>
+            <column name="REALM_ID" type="VARCHAR(255)"/>
+            <column name="SESSION_ID" type="VARCHAR(255)"/>
+            <column name="TIME" type="BIGINT(19)"/>
+            <column name="TYPE" type="VARCHAR(255)"/>
+            <column name="USER_ID" type="VARCHAR(255)"/>
+        </createTable>
+        <createTable tableName="FED_PROVIDERS">
+            <column name="REALM_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="USERFEDERATIONPROVIDERS_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+        <createTable tableName="KEYCLOAK_ROLE">
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="APP_REALM_CONSTRAINT" type="VARCHAR(36)"/>
+            <column name="APPLICATION_ROLE" type="BOOLEAN(1)"/>
+            <column name="DESCRIPTION" type="VARCHAR(255)"/>
+            <column name="NAME" type="VARCHAR(255)"/>
+            <column name="REALM_ID" type="VARCHAR(255)"/>
+            <column name="APPLICATION" type="VARCHAR(36)"/>
+            <column name="REALM" type="VARCHAR(36)"/>
+        </createTable>
+        <createTable tableName="REALM">
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="ACCESS_CODE_LIFESPAN" type="INT(10)"/>
+            <column name="USER_ACTION_LIFESPAN" type="INT(10)"/>
+            <column name="ACCESS_TOKEN_LIFESPAN" type="INT(10)"/>
+            <column name="ACCOUNT_THEME" type="VARCHAR(255)"/>
+            <column name="ADMIN_THEME" type="VARCHAR(255)"/>
+            <column name="EMAIL_THEME" type="VARCHAR(255)"/>
+            <column name="ENABLED" type="BOOLEAN(1)"/>
+            <column name="EVENTS_ENABLED" type="BOOLEAN(1)"/>
+            <column name="EVENTS_EXPIRATION" type="BIGINT(19)"/>
+            <column name="LOGIN_THEME" type="VARCHAR(255)"/>
+            <column name="NAME" type="VARCHAR(255)"/>
+            <column name="NOT_BEFORE" type="INT(10)"/>
+            <column name="PASSWORD_CRED_GRANT_ALLOWED" type="BOOLEAN(1)"/>
+            <column name="PASSWORD_POLICY" type="VARCHAR(255)"/>
+            <column name="PRIVATE_KEY" type="VARCHAR(2048)"/>
+            <column name="PUBLIC_KEY" type="VARCHAR(2048)"/>
+            <column name="REGISTRATION_ALLOWED" type="BOOLEAN(1)"/>
+            <column name="REMEMBER_ME" type="BOOLEAN(1)"/>
+            <column name="RESET_PASSWORD_ALLOWED" type="BOOLEAN(1)"/>
+            <column name="SOCIAL" type="BOOLEAN(1)"/>
+            <column name="SSL_REQUIRED" type="VARCHAR(255)"/>
+            <column name="SSO_IDLE_TIMEOUT" type="INT(10)"/>
+            <column name="SSO_MAX_LIFESPAN" type="INT(10)"/>
+            <column name="UPDATE_PROFILE_ON_SOC_LOGIN" type="BOOLEAN(1)"/>
+            <column name="VERIFY_EMAIL" type="BOOLEAN(1)"/>
+            <column name="MASTER_ADMIN_APP" type="VARCHAR(36)"/>
+        </createTable>
+        <createTable tableName="REALM_APPLICATION">
+            <column name="APPLICATION_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="REALM_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+        <createTable tableName="REALM_ATTRIBUTE">
+            <column name="NAME" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="VALUE" type="VARCHAR(255)"/>
+            <column name="REALM_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+        <createTable tableName="REALM_DEFAULT_ROLES">
+            <column name="REALM_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="ROLE_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+        <createTable tableName="REALM_EVENTS_LISTENERS">
+            <column name="REALM_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="VALUE" type="VARCHAR(255)"/>
+        </createTable>
+        <createTable tableName="REALM_REQUIRED_CREDENTIAL">
+            <column name="TYPE" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="FORM_LABEL" type="VARCHAR(255)"/>
+            <column name="INPUT" type="BOOLEAN(1)"/>
+            <column name="SECRET" type="BOOLEAN(1)"/>
+            <column name="REALM_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+        <createTable tableName="REALM_SMTP_CONFIG">
+            <column name="REALM_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="VALUE" type="VARCHAR(255)"/>
+            <column name="NAME" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+        <createTable tableName="REALM_SOCIAL_CONFIG">
+            <column name="REALM_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="VALUE" type="VARCHAR(255)"/>
+            <column name="NAME" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+        <createTable tableName="REDIRECT_URIS">
+            <column name="CLIENT_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="VALUE" type="VARCHAR(255)"/>
+        </createTable>
+        <createTable tableName="SCOPE_MAPPING">
+            <column name="CLIENT_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="ROLE_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+        <createTable tableName="USERNAME_LOGIN_FAILURE">
+            <column name="REALM_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="USERNAME" type="VARCHAR(200)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="FAILED_LOGIN_NOT_BEFORE" type="INT(10)"/>
+            <column name="LAST_FAILURE" type="BIGINT(19)"/>
+            <column name="LAST_IP_FAILURE" type="VARCHAR(255)"/>
+            <column name="NUM_FAILURES" type="INT(10)"/>
+        </createTable>
+        <createTable tableName="USER_ATTRIBUTE">
+            <column name="NAME" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="VALUE" type="VARCHAR(255)"/>
+            <column name="USER_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+        <createTable tableName="USER_ENTITY">
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="EMAIL" type="VARCHAR(255)"/>
+            <column name="EMAIL_CONSTRAINT" type="VARCHAR(255)"/>
+            <column name="EMAIL_VERIFIED" type="BOOLEAN(1)"/>
+            <column name="ENABLED" type="BOOLEAN(1)"/>
+            <column name="FEDERATION_LINK" type="VARCHAR(255)"/>
+            <column name="FIRST_NAME" type="VARCHAR(255)"/>
+            <column name="LAST_NAME" type="VARCHAR(255)"/>
+            <column name="REALM_ID" type="VARCHAR(255)"/>
+            <column name="TOTP" type="BOOLEAN(1)"/>
+            <column name="USERNAME" type="VARCHAR(255)"/>
+        </createTable>
+        <createTable tableName="USER_FEDERATION_CONFIG">
+            <column name="USER_FEDERATION_PROVIDER_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="VALUE" type="VARCHAR(255)"/>
+            <column name="NAME" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+        <createTable tableName="USER_FEDERATION_PROVIDER">
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="CHANGED_SYNC_PERIOD" type="INT(10)"/>
+            <column name="DISPLAY_NAME" type="VARCHAR(255)"/>
+            <column name="FULL_SYNC_PERIOD" type="INT(10)"/>
+            <column name="LAST_SYNC" type="INT(10)"/>
+            <column name="PRIORITY" type="INT(10)"/>
+            <column name="PROVIDER_NAME" type="VARCHAR(255)"/>
+            <column name="REALM_ID" type="VARCHAR(36)"/>
+        </createTable>
+        <createTable tableName="USER_REQUIRED_ACTION">
+            <column name="ACTION" type="INT(10)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="USER_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+        <createTable tableName="USER_ROLE_MAPPING">
+            <column name="ROLE_ID" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="USER_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+        <createTable tableName="USER_SESSION">
+            <column name="ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="AUTH_METHOD" type="VARCHAR(255)"/>
+            <column name="IP_ADDRESS" type="VARCHAR(255)"/>
+            <column name="LAST_SESSION_REFRESH" type="INT(10)"/>
+            <column name="LOGIN_USERNAME" type="VARCHAR(255)"/>
+            <column name="REALM_ID" type="VARCHAR(255)"/>
+            <column name="REMEMBER_ME" type="BOOLEAN(1)"/>
+            <column name="STARTED" type="INT(10)"/>
+            <column name="USER_ID" type="VARCHAR(255)"/>
+        </createTable>
+        <createTable tableName="USER_SOCIAL_LINK">
+            <column name="SOCIAL_PROVIDER" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="REALM_ID" type="VARCHAR(255)"/>
+            <column name="SOCIAL_USER_ID" type="VARCHAR(255)"/>
+            <column name="SOCIAL_USERNAME" type="VARCHAR(255)"/>
+            <column name="USER_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+        <createTable tableName="WEB_ORIGINS">
+            <column name="CLIENT_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="VALUE" type="VARCHAR(255)"/>
+        </createTable>
+        <addPrimaryKey columnNames="REALM_ID, NAME" constraintName="CONSTRAINT_1" tableName="REALM_SOCIAL_CONFIG"/>
+        <addPrimaryKey columnNames="REALM_ID, USERNAME" constraintName="CONSTRAINT_17" tableName="USERNAME_LOGIN_FAILURE"/>
+        <addPrimaryKey columnNames="ACTION, USER_ID" constraintName="CONSTRAINT_2" tableName="USER_REQUIRED_ACTION"/>
+        <addPrimaryKey columnNames="SOCIAL_PROVIDER, USER_ID" constraintName="CONSTRAINT_3" tableName="USER_SOCIAL_LINK"/>
+        <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_4" tableName="EVENT_ENTITY"/>
+        <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_4A" tableName="REALM"/>
+        <addPrimaryKey columnNames="CLIENT_SESSION, ROLE_ID" constraintName="CONSTRAINT_5" tableName="CLIENT_SESSION_ROLE"/>
+        <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_57" tableName="USER_SESSION"/>
+        <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_5C" tableName="USER_FEDERATION_PROVIDER"/>
+        <addPrimaryKey columnNames="NAME, USER_ID" constraintName="CONSTRAINT_6" tableName="USER_ATTRIBUTE"/>
+        <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_7" tableName="CLIENT"/>
+        <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_8" tableName="CLIENT_SESSION"/>
+        <addPrimaryKey columnNames="CLIENT_ID, ROLE_ID" constraintName="CONSTRAINT_81" tableName="SCOPE_MAPPING"/>
+        <addPrimaryKey columnNames="NAME, REALM_ID" constraintName="CONSTRAINT_9" tableName="REALM_ATTRIBUTE"/>
+        <addPrimaryKey columnNames="REALM_ID, TYPE" constraintName="CONSTRAINT_92" tableName="REALM_REQUIRED_CREDENTIAL"/>
+        <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_A" tableName="KEYCLOAK_ROLE"/>
+        <addPrimaryKey columnNames="ROLE_ID, USER_ID" constraintName="CONSTRAINT_C" tableName="USER_ROLE_MAPPING"/>
+        <addPrimaryKey columnNames="REALM_ID, NAME" constraintName="CONSTRAINT_E" tableName="REALM_SMTP_CONFIG"/>
+        <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_F" tableName="CREDENTIAL"/>
+        <addPrimaryKey columnNames="USER_FEDERATION_PROVIDER_ID, NAME" constraintName="CONSTRAINT_F9" tableName="USER_FEDERATION_CONFIG"/>
+        <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_FB" tableName="USER_ENTITY"/>
+        <addUniqueConstraint columnNames="ROLE_ID" constraintName="UK_8AELWNIBJI49AVXSRTUF6XJOW" deferrable="false" disabled="false" initiallyDeferred="false" tableName="APPLICATION_DEFAULT_ROLES"/>
+        <addUniqueConstraint columnNames="REALM_ID,NAME" constraintName="UK_B71CJLBENV945RB6GCON438AT" deferrable="false" disabled="false" initiallyDeferred="false" tableName="CLIENT"/>
+        <addUniqueConstraint columnNames="USERFEDERATIONPROVIDERS_ID" constraintName="UK_DCCIRJLIPU1478VQC89DID88C" deferrable="false" disabled="false" initiallyDeferred="false" tableName="FED_PROVIDERS"/>
+        <addUniqueConstraint columnNames="REALM_ID,EMAIL_CONSTRAINT" constraintName="UK_DYKN684SL8UP1CRFEI6ECKHD7" deferrable="false" disabled="false" initiallyDeferred="false" tableName="USER_ENTITY"/>
+        <addUniqueConstraint columnNames="ROLE_ID" constraintName="UK_H4WPD7W4HSOOLNI3H0SW7BTJE" deferrable="false" disabled="false" initiallyDeferred="false" tableName="REALM_DEFAULT_ROLES"/>
+        <addUniqueConstraint columnNames="NAME,APP_REALM_CONSTRAINT" constraintName="UK_J3RWUVD56ONTGSUHOGM184WW2" deferrable="false" disabled="false" initiallyDeferred="false" tableName="KEYCLOAK_ROLE"/>
+        <addUniqueConstraint columnNames="REALM_ID" constraintName="UK_L5QGA3RFME47335JY8JXYXH3I" deferrable="false" disabled="false" initiallyDeferred="false" tableName="REALM_APPLICATION"/>
+        <addUniqueConstraint columnNames="NAME" constraintName="UK_ORVSDMLA56612EAEFIQ6WL5OI" deferrable="false" disabled="false" initiallyDeferred="false" tableName="REALM"/>
+        <addUniqueConstraint columnNames="REALM_ID,USERNAME" constraintName="UK_RU8TT6T700S9V50BU18WS5HA6" deferrable="false" disabled="false" initiallyDeferred="false" tableName="USER_ENTITY"/>
+        <addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_SESSION_ROLE" constraintName="FK_11B7SGQW18I532811V7O2DV76" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/>
+        <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="REDIRECT_URIS" constraintName="FK_1BURS8PB4OUJ97H5WUPPAHV9F" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="USER_FEDERATION_PROVIDER" constraintName="FK_1FJ32F6PTOLW2QY60CD8N01E8" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="FED_PROVIDERS" constraintName="FK_213LYQ09FKXQ8K8NY8DY3737T" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_REQUIRED_CREDENTIAL" constraintName="FK_5HG65LYBEVAVKQFKI3KPONH9V" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="USER_ATTRIBUTE" constraintName="FK_5HRM2VLF9QL5FU043KQEPOVBR" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
+        <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="USER_SOCIAL_LINK" constraintName="FK_68CJYS5UWM55UY823Y75XG4OM" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
+        <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="USER_REQUIRED_ACTION" constraintName="FK_6QJ3W1JW9CVAFHE19BWSIUVMD" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
+        <addForeignKeyConstraint baseColumnNames="REALM" baseTableName="KEYCLOAK_ROLE" constraintName="FK_6VYQFE4CN4WLQ8R6KT5VDSJ5C" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_SMTP_CONFIG" constraintName="FK_70EJ8XDXGXD0B9HH6180IRR0O" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="APPLICATION_ID" baseTableName="REALM_APPLICATION" constraintName="FK_71S3P0DIUXAWWQQSA528UBY2Q" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="ROLE_ID" baseTableName="APPLICATION_DEFAULT_ROLES" constraintName="FK_8AELWNIBJI49AVXSRTUF6XJOW" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_ATTRIBUTE" constraintName="FK_8SHXD6L3E9ATQUKACXGPFFPTW" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="COMPOSITE" baseTableName="COMPOSITE_ROLE" constraintName="FK_A63WVEKFTU8JO1PNJ81E7MCE2" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
+        <addForeignKeyConstraint baseColumnNames="SESSION_ID" baseTableName="CLIENT_SESSION" constraintName="FK_B4AO2VCVAT6UKAU74WBWTFQO1" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_SESSION"/>
+        <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="USER_ROLE_MAPPING" constraintName="FK_C4FQV34P1MBYLLOXANG7B1Q3L" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
+        <addForeignKeyConstraint baseColumnNames="USERFEDERATIONPROVIDERS_ID" baseTableName="FED_PROVIDERS" constraintName="FK_DCCIRJLIPU1478VQC89DID88C" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_FEDERATION_PROVIDER"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_DEFAULT_ROLES" constraintName="FK_EVUDB1PPW84OXFAX2DRS03ICC" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="CHILD_ROLE" baseTableName="COMPOSITE_ROLE" constraintName="FK_GR7THLLB9LU8Q4VQA4524JJY8" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
+        <addForeignKeyConstraint baseColumnNames="ROLE_ID" baseTableName="REALM_DEFAULT_ROLES" constraintName="FK_H4WPD7W4HSOOLNI3H0SW7BTJE" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_EVENTS_LISTENERS" constraintName="FK_H846O4H0W8EPX5NXEV9F5Y69J" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_APPLICATION" constraintName="FK_L5QGA3RFME47335JY8JXYXH3I" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+        <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="WEB_ORIGINS" constraintName="FK_LOJPHO213XCX4WNKOG82SSRFY" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+        <addForeignKeyConstraint baseColumnNames="APPLICATION_ID" baseTableName="APPLICATION_DEFAULT_ROLES" constraintName="FK_MAYLTS7KLWQW2H8M2B5JOYTKY" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+        <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="SCOPE_MAPPING" constraintName="FK_OUSE064PLMLR732LXJCN1Q5F1" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+        <addForeignKeyConstraint baseColumnNames="ROLE_ID" baseTableName="SCOPE_MAPPING" constraintName="FK_P3RH9GRKU11KQFRS4FLTT7RNQ" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="KEYCLOAK_ROLE"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="CLIENT" constraintName="FK_P56CTINXXB9GSK57FO49F9TAC" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="CREDENTIAL" constraintName="FK_PFYR0GLASQYL0DEI3KL69R6V0" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
+        <addForeignKeyConstraint baseColumnNames="APPLICATION" baseTableName="KEYCLOAK_ROLE" constraintName="FK_PIMO5LE2C0RAL09FL8CM9WFW9" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+        <addForeignKeyConstraint baseColumnNames="MASTER_ADMIN_APP" baseTableName="REALM" constraintName="FK_RSAF444KK6QRKMS7N56AIWQ5Y" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+        <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REALM_SOCIAL_CONFIG" constraintName="FK_SV5I3C2TI7G0G922FGE683SOV" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="REALM"/>
+        <addForeignKeyConstraint baseColumnNames="USER_FEDERATION_PROVIDER_ID" baseTableName="USER_FEDERATION_CONFIG" constraintName="FK_T13HPU1J94R2EBPEKR39X5EU5" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="USER_FEDERATION_PROVIDER"/>
+    </changeSet>
+</databaseChangeLog>
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.1.0.Beta1.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.1.0.Beta1.xml
new file mode 100644
index 0000000..0b9f0f5
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.1.0.Beta1.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.2.xsd">
+    <changeSet author="sthorger@redhat.com" id="1.1.0.Beta1">
+        <delete tableName="CLIENT_SESSION_ROLE"/>
+        <delete tableName="CLIENT_SESSION"/>
+        <delete tableName="USER_SESSION"/>
+
+        <createTable tableName="CLIENT_ATTRIBUTES">
+            <column name="CLIENT_ID" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="VALUE" type="VARCHAR(2048)"/>
+            <column name="NAME" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+        <createTable tableName="CLIENT_SESSION_NOTE">
+            <column name="NAME" type="VARCHAR(255)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="VALUE" type="VARCHAR(255)"/>
+            <column name="CLIENT_SESSION" type="VARCHAR(36)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+        <addColumn tableName="CLIENT_SESSION">
+            <column name="AUTH_METHOD" type="VARCHAR(255)"/>
+        </addColumn>
+        <addColumn tableName="CLIENT">
+            <column name="PROTOCOL" type="VARCHAR(255)"/>
+        </addColumn>
+        <addColumn tableName="CLIENT_SESSION">
+            <column name="REALM_ID" type="VARCHAR(255)"/>
+        </addColumn>
+        <addPrimaryKey columnNames="CLIENT_ID, NAME" constraintName="CONSTRAINT_3C" tableName="CLIENT_ATTRIBUTES"/>
+        <addPrimaryKey columnNames="CLIENT_SESSION, NAME" constraintName="CONSTRAINT_5E" tableName="CLIENT_SESSION_NOTE"/>
+        <addForeignKeyConstraint baseColumnNames="CLIENT_ID" baseTableName="CLIENT_ATTRIBUTES" constraintName="FK3C47C64BEACCA966" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT"/>
+        <addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_SESSION_NOTE" constraintName="FK5EDFB00FF51C2736" deferrable="false" initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/>
+    </changeSet>
+</databaseChangeLog>
\ No newline at end of file
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml
new file mode 100644
index 0000000..010b121
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.2.xsd">
+    <include file="META-INF/jpa-changelog-1.0.0.Final.xml"/>
+    <include file="META-INF/jpa-changelog-1.1.0.Beta1.xml"/>
+</databaseChangeLog>
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/services/org.keycloak.connections.jpa.updater.JpaUpdaterProviderFactory b/connections/jpa-liquibase/src/main/resources/META-INF/services/org.keycloak.connections.jpa.updater.JpaUpdaterProviderFactory
new file mode 100644
index 0000000..cb36ec3
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/services/org.keycloak.connections.jpa.updater.JpaUpdaterProviderFactory
@@ -0,0 +1 @@
+org.keycloak.connections.jpa.updater.liquibase.LiquibaseJpaUpdaterProviderFactory
\ No newline at end of file
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
index e644c7a..f800dfb 100644
--- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
@@ -9,6 +9,7 @@ import org.keycloak.Config;
 import org.keycloak.connections.mongo.api.MongoStore;
 import org.keycloak.connections.mongo.impl.MongoStoreImpl;
 import org.keycloak.connections.mongo.impl.context.TransactionMongoStoreInvocationContext;
+import org.keycloak.connections.mongo.updater.DefaultMongoUpdaterProvider;
 import org.keycloak.models.KeycloakSession;
 
 import java.util.Collections;
@@ -64,7 +65,6 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
                         String host = config.get("host", ServerAddress.defaultHost());
                         int port = config.getInt("port", ServerAddress.defaultPort());
                         String dbName = config.get("db", "keycloak");
-                        boolean clearOnStartup = config.getBoolean("clearOnStartup", false);
 
                         String user = config.get("user");
                         String password = config.get("password");
@@ -77,9 +77,18 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
 
                         this.db = client.getDB(dbName);
 
-                        this.mongoStore = new MongoStoreImpl(db, clearOnStartup, getManagedEntities());
+                        String databaseSchema = config.get("databaseSchema");
+                        if (databaseSchema != null) {
+                            if (databaseSchema.equals("update")) {
+                                new DefaultMongoUpdaterProvider().update(db);
+                            } else {
+                                throw new RuntimeException("Invalid value for databaseSchema: " + databaseSchema);
+                            }
+                        }
+
+                        this.mongoStore = new MongoStoreImpl(db, getManagedEntities());
 
-                        logger.infof("Initialized mongo model. host: %s, port: %d, db: %s, clearOnStartup: %b", host, port, dbName, clearOnStartup);
+                        logger.debugv("Initialized mongo model. host: %s, port: %d, db: %s", host, port, dbName);
                     } catch (Exception e) {
                         throw new RuntimeException(e);
                     }
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/MongoStoreImpl.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/MongoStoreImpl.java
index 09104ea..85bf25e 100755
--- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/MongoStoreImpl.java
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/MongoStoreImpl.java
@@ -11,8 +11,6 @@ import org.jboss.logging.Logger;
 import org.keycloak.connections.mongo.api.MongoCollection;
 import org.keycloak.connections.mongo.api.MongoEntity;
 import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
-import org.keycloak.connections.mongo.api.MongoIndex;
-import org.keycloak.connections.mongo.api.MongoIndexes;
 import org.keycloak.connections.mongo.api.MongoStore;
 import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.connections.mongo.api.context.MongoTask;
@@ -57,7 +55,7 @@ public class MongoStoreImpl implements MongoStore {
             new ConcurrentHashMap<Class<?>, EntityInfo>();
 
 
-    public MongoStoreImpl(DB database, boolean clearCollectionsOnStartup, Class<?>[] managedEntityTypes) {
+    public MongoStoreImpl(DB database, Class<?>[] managedEntityTypes) {
         this.database = database;
 
         mapperRegistry = new MapperRegistry();
@@ -86,13 +84,6 @@ public class MongoStoreImpl implements MongoStore {
             mapperRegistry.addAppObjectMapper(new MongoEntityMapper(this, mapperRegistry, type));
             mapperRegistry.addDBObjectMapper(new BasicDBObjectMapper(this, mapperRegistry, type));
         }
-
-        if (clearCollectionsOnStartup) {
-            // dropDatabase();
-            clearManagedCollections(managedEntityTypes);
-        }
-
-        initManagedCollections(managedEntityTypes);
     }
 
     protected void dropDatabase() {
@@ -100,63 +91,6 @@ public class MongoStoreImpl implements MongoStore {
         logger.info("Database " + this.database.getName() + " dropped in MongoDB");
     }
 
-    // Don't drop database, but just clear all data in managed collections (useful for export/import or during development)
-    protected void clearManagedCollections(Class<?>[] managedEntityTypes) {
-        for (Class<?> clazz : managedEntityTypes) {
-            DBCollection dbCollection = getDBCollectionForType(clazz);
-            if (dbCollection != null) {
-                dbCollection.remove(new BasicDBObject());
-                logger.debug("Collection " + dbCollection.getName() + " cleared from " + this.database.getName());
-            }
-        }
-    }
-
-    protected void initManagedCollections(Class<?>[] managedEntityTypes) {
-        for (Class<?> clazz : managedEntityTypes) {
-            EntityInfo entityInfo = getEntityInfo(clazz);
-            String dbCollectionName = entityInfo.getDbCollectionName();
-            if (dbCollectionName != null && !database.collectionExists(dbCollectionName)) {
-                DBCollection dbCollection = database.getCollection(dbCollectionName);
-
-                logger.debug("Created collection " + dbCollection.getName() + " in " + this.database.getName());
-
-                MongoIndex index = clazz.getAnnotation(MongoIndex.class);
-                if (index != null) {
-                    createIndex(dbCollection, index);
-                }
-
-                MongoIndexes indexes = clazz.getAnnotation(MongoIndexes.class);
-                if (indexes != null) {
-                    for (MongoIndex i : indexes.value()) {
-                        createIndex(dbCollection, i);
-                    }
-                }
-            }
-        }
-    }
-
-    protected void createIndex(DBCollection dbCollection, MongoIndex index) {
-        BasicDBObject fields = new BasicDBObject();
-        for (String f : index.fields()) {
-            fields.put(f, 1);
-        }
-
-        boolean unique = index.unique();
-        boolean sparse = index.sparse();
-
-        BasicDBObject options = new BasicDBObject();
-        if (unique) {
-            options.put("unique", unique);
-        }
-        if (sparse) {
-            options.put("sparse", sparse);
-        }
-
-        dbCollection.ensureIndex(fields, options);
-
-        logger.debug("Created index " + fields + "(options: " + options + ") on " + dbCollection.getName() + " in " + this.database.getName());
-    }
-
     @Override
     public void insertEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context) {
         Class<? extends MongoEntity> clazz = entity.getClass();
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/DefaultMongoUpdaterProvider.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/DefaultMongoUpdaterProvider.java
new file mode 100644
index 0000000..1f3f6ae
--- /dev/null
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/DefaultMongoUpdaterProvider.java
@@ -0,0 +1,103 @@
+package org.keycloak.connections.mongo.updater;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DB;
+import com.mongodb.DBCollection;
+import com.mongodb.DBCursor;
+import com.mongodb.DBObject;
+import org.jboss.logging.Logger;
+import org.keycloak.connections.mongo.updater.updates.Update;
+import org.keycloak.connections.mongo.updater.updates.Update1_0_0_Final;
+import org.keycloak.connections.mongo.updater.updates.Update1_1_0_Beta1;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class DefaultMongoUpdaterProvider implements MongoUpdaterProvider {
+
+    public static final Logger log = Logger.getLogger(DefaultMongoUpdaterProvider.class);
+
+    public static final String CHANGE_LOG_COLLECTION = "databaseChangeLog";
+
+    private Class<? extends Update>[] updates = new Class[]{
+            Update1_0_0_Final.class,
+            Update1_1_0_Beta1.class
+    };
+
+    @Override
+    public void update(DB db) {
+        log.debug("Starting database update");
+        try {
+            boolean changeLogExists = db.collectionExists(CHANGE_LOG_COLLECTION);
+            boolean realmExists = db.collectionExists("realms");
+
+            DBCollection changeLog = db.getCollection(CHANGE_LOG_COLLECTION);
+
+            List<String> executed = new LinkedList<String>();
+            if (!changeLogExists && realmExists) {
+                Update1_0_0_Final u = new Update1_0_0_Final();
+                executed.add(u.getId());
+                createLog(changeLog, u, 1);
+            } else if (changeLogExists) {
+                DBCursor cursor = changeLog.find().sort(new BasicDBObject("orderExecuted", 1));
+                while (cursor.hasNext()) {
+                    executed.add((String) cursor.next().get("_id"));
+                }
+            }
+
+            List<Update> updatesToRun = new LinkedList<Update>();
+            for (Class<? extends Update> updateClass : updates) {
+                Update u = updateClass.newInstance();
+                if (!executed.contains(u.getId())) {
+                    updatesToRun.add(u);
+                }
+            }
+
+            if (!updatesToRun.isEmpty()) {
+                if (executed.isEmpty()) {
+                    log.info("Initializing database schema");
+                } else {
+                    if (log.isDebugEnabled()) {
+                        log.infov("Updating database from {0} to {1}", executed.get(executed.size() - 1), updatesToRun.get(updatesToRun.size() - 1).getId());
+                    } else {
+                        log.debugv("Updating database");
+                    }
+                }
+
+                int order = executed.size();
+                for (Update u : updatesToRun) {
+                    log.debugv("Executing updates for {0}", u.getId());
+
+                    u.setLog(log);
+                    u.setDb(db);
+                    u.update();
+
+                    createLog(changeLog, u, ++order);
+
+                    log.debugv("Completed updates for {0}", u.getId());
+                }
+            }
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to update database", e);
+        }
+        log.debug("Completed database update");
+    }
+
+    private void createLog(DBCollection changeLog, Update update, int orderExecuted) {
+        changeLog.insert(new BasicDBObject("_id", update.getId()).append("dateExecuted", new Date()).append("orderExecuted", orderExecuted));
+    }
+
+    @Override
+    public void close() {
+    }
+
+}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/DefaultMongoUpdaterProviderFactory.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/DefaultMongoUpdaterProviderFactory.java
new file mode 100644
index 0000000..8069850
--- /dev/null
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/DefaultMongoUpdaterProviderFactory.java
@@ -0,0 +1,29 @@
+package org.keycloak.connections.mongo.updater;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class DefaultMongoUpdaterProviderFactory implements MongoUpdaterProviderFactory {
+
+    @Override
+    public MongoUpdaterProvider create(KeycloakSession session) {
+        return new DefaultMongoUpdaterProvider();
+    }
+
+    @Override
+    public void init(Config.Scope config) {
+    }
+
+    @Override
+    public void close() {
+    }
+
+    @Override
+    public String getId() {
+        return "default";
+    }
+
+}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterProvider.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterProvider.java
new file mode 100644
index 0000000..7192f1d
--- /dev/null
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterProvider.java
@@ -0,0 +1,13 @@
+package org.keycloak.connections.mongo.updater;
+
+import com.mongodb.DB;
+import org.keycloak.provider.Provider;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface MongoUpdaterProvider extends Provider {
+
+    public void update(DB db);
+
+}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterProviderFactory.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterProviderFactory.java
new file mode 100644
index 0000000..981f6d0
--- /dev/null
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterProviderFactory.java
@@ -0,0 +1,9 @@
+package org.keycloak.connections.mongo.updater;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface MongoUpdaterProviderFactory extends ProviderFactory<MongoUpdaterProvider> {
+}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterSpi.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterSpi.java
new file mode 100644
index 0000000..da89b4d
--- /dev/null
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/MongoUpdaterSpi.java
@@ -0,0 +1,27 @@
+package org.keycloak.connections.mongo.updater;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class MongoUpdaterSpi implements Spi {
+
+    @Override
+    public String getName() {
+        return "connectionsMongoUpdater";
+    }
+
+    @Override
+    public Class<? extends Provider> getProviderClass() {
+        return MongoUpdaterProvider.class;
+    }
+
+    @Override
+    public Class<? extends ProviderFactory> getProviderFactoryClass() {
+        return MongoUpdaterProviderFactory.class;
+    }
+
+}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update.java
new file mode 100644
index 0000000..e8058f0
--- /dev/null
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update.java
@@ -0,0 +1,62 @@
+package org.keycloak.connections.mongo.updater.updates;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DB;
+import com.mongodb.DBCollection;
+import org.jboss.logging.Logger;
+
+import java.util.Arrays;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public abstract class Update {
+
+    protected DB db;
+
+    protected Logger log;
+
+    public abstract String getId();
+
+    public abstract void update() throws ClassNotFoundException;
+
+    protected DBCollection createCollection(String name) {
+        if (db.collectionExists(name)) {
+            throw new RuntimeException("Failed to create collection {0}: collection already exists");
+        }
+
+        DBCollection col = db.getCollection(name);
+        log.debugv("Created collection {0}", name);
+        return col;
+    }
+
+    protected void ensureIndex(String name, String field, boolean unique, boolean sparse) {
+        ensureIndex(name, new String[]{field}, unique, sparse);
+    }
+
+    protected void ensureIndex(String name, String[] fields, boolean unique, boolean sparse) {
+        DBCollection col = db.getCollection(name);
+
+        BasicDBObject o = new BasicDBObject();
+        for (String f : fields) {
+            o.append(f, 1);
+        }
+
+        col.ensureIndex(o, new BasicDBObject("unique", unique).append("sparse", sparse));
+        log.debugv("Created index {0}, fields={1}, unique={2}, sparse={3}", name, Arrays.toString(fields), unique, sparse);
+    }
+
+    protected void deleteEntries(String collection) {
+        db.getCollection(collection).remove(new BasicDBObject());
+        log.debugv("Deleted entries from {0}", collection);
+    }
+
+    public void setLog(Logger log) {
+        this.log = log;
+    }
+
+    public void setDb(DB db) {
+        this.db = db;
+    }
+
+}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_0_0_Final.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_0_0_Final.java
new file mode 100644
index 0000000..ded25dc
--- /dev/null
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_0_0_Final.java
@@ -0,0 +1,44 @@
+package org.keycloak.connections.mongo.updater.updates;
+
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBCollection;
+import org.keycloak.connections.mongo.updater.DefaultMongoUpdaterProvider;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class Update1_0_0_Final extends Update {
+
+    @Override
+    public String getId() {
+        return "1.0.0.Final";
+    }
+
+    @Override
+    public void update() throws ClassNotFoundException {
+        DBCollection realmsCollection = db.getCollection("realms");
+        realmsCollection.ensureIndex(new BasicDBObject("name", 1), new BasicDBObject("unique", true));
+
+        DefaultMongoUpdaterProvider.log.debugv("Created collection {0}", "realms");
+
+        createCollection("users");
+        ensureIndex("users", new String[] { "realmId", "username"}, true, false);
+        ensureIndex("users", "emailIndex", true, true);
+
+        createCollection("roles");
+        ensureIndex("roles", "nameIndex", true, false);
+
+        createCollection("applications");
+        ensureIndex("applications", new String[]{"realmId", "name"}, true, false);
+
+        createCollection("oauthClients");
+        ensureIndex("oauthClients", new String[] { "realmId", "name"}, true, false);
+
+        createCollection("userFailures");
+
+        createCollection("sessions");
+
+        createCollection("clientSessions");
+    }
+
+}
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_1_0_Beta1.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_1_0_Beta1.java
new file mode 100644
index 0000000..89d372f
--- /dev/null
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/updater/updates/Update1_1_0_Beta1.java
@@ -0,0 +1,19 @@
+package org.keycloak.connections.mongo.updater.updates;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class Update1_1_0_Beta1 extends Update {
+
+    @Override
+    public String getId() {
+        return "1.1.0.Beta1";
+    }
+
+    @Override
+    public void update() {
+        deleteEntries("clientSessions");
+        deleteEntries("sessions");
+    }
+
+}
diff --git a/connections/pom.xml b/connections/pom.xml
index a5b3bb2..2d284bb 100755
--- a/connections/pom.xml
+++ b/connections/pom.xml
@@ -14,6 +14,7 @@
 
     <modules>
         <module>jpa</module>
+        <module>jpa-liquibase</module>
         <module>infinispan</module>
         <module>mongo</module>
     </modules>
diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml
index a812b08..9c20bd4 100755
--- a/dependencies/server-all/pom.xml
+++ b/dependencies/server-all/pom.xml
@@ -28,6 +28,11 @@
         </dependency>
         <dependency>
             <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-connections-jpa-liquibase</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
             <artifactId>keycloak-connections-infinispan</artifactId>
             <version>${project.version}</version>
         </dependency>
@@ -180,6 +185,17 @@
             <groupId>de.idyl</groupId>
             <artifactId>winzipaes</artifactId>
         </dependency>
+
+        <dependency>
+            <groupId>org.liquibase</groupId>
+            <artifactId>liquibase-core</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.yaml</groupId>
+                    <artifactId>snakeyaml</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
     </dependencies>
 
 </project>
\ No newline at end of file
diff --git a/misc/UpdatingDatabaseSchema.md b/misc/UpdatingDatabaseSchema.md
new file mode 100644
index 0000000..1dcee57
--- /dev/null
+++ b/misc/UpdatingDatabaseSchema.md
@@ -0,0 +1,79 @@
+Updating Database Schema
+========================
+
+Keycloak supports automatically migrating the database to a new version. This is done by applying one or more change-sets
+to the existing database. This means if you need to do any changes to database schemas for JPA or Mongo you need to create 
+a change-set that can transform the schema as well as any existing data.
+
+This includes changes to:
+ 
+* Realm entities
+* User entities
+* User session entities
+* Event entities
+
+ 
+Creating a JPA change-set
+-------------------------
+
+We use Liquibase to support updating the database. The change-sets are located in 
+`connections/jpa-liquibase/src/main/resources/META-INF`. There's a separate file for each release that requires database
+changes.
+
+To manually create a change-set add a new file in the above location with the name `jpa-changelog-<version>.xml`. This file 
+should contain a single `change-set` with `id` equal to the next version to be released and `author` set to your email 
+address. Then look at Liquibase documentation on how to write this file. Add a reference to this file in `jpa-changelog-master.xml`. 
+The file should have a single change-set and the id of the change-set should be the next version to be released. 
+
+You also need to update `org.keycloak.connections.jpa.updater.JpaUpdaterProvider#LAST_VERSION`. This
+is used by Keycloak to quickly determine if the database is up to date or not.
+  
+You can also have Liquibase and Hibernate create one for you. To do this follow these steps:
+
+1. Delete existing databases  
+   `rm keycloak*h2.db`
+2. Create a database of the old format:  
+   `mvn -f connections/jpa-liquibase/pom.xml liquibase:update -Durl=jdbc:h2:keycloak`
+3. Make a copy of the database:  
+   `cp keycloak.h2.db keycloak-old.h2.db`    
+3. Run KeycloakServer to make Hibernate update the schema:  
+   `mvn -f testsuite/integration exec:java -Pkeycloak-server -Dkeycloak.connectionsJpa.url='jdbc:h2:keycloak' -Dkeycloak.connectionsJpa.databaseSchema='development-update'`
+4. Wait until server is completely started, then stop it
+5. View the difference:                                       
+   `mvn -f connections/jpa-liquibase/pom.xml liquibase:diff -Durl=jdbc:h2:keycloak-old -DreferenceUrl=jdbc:h2:keycloak`
+6. Create a change-set file:
+   `mvn -f connections/jpa-liquibase/pom.xml liquibase:diff -Durl=jdbc:h2:keycloak-old -DreferenceUrl=jdbc:h2:keycloak -Dliquibase.diffChangeLogFile=changelog.xml`    
+    
+This will generate the file `changelog.xml`. Once it's generated edit the file and combine all `change-sets` into
+a single `change-set` and change the `id` to the next version to be released and `author` to your email address. Then
+follow the steps above to copy it to the correct location and update `jpa-changelog-master.xml`. You have to manually
+add entries to the `change-set` to update existing data if required. 
+
+When you have update the change-set Hibernate can validate the schema for you. First run:
+
+    rm -rf keycloak*h2.db
+    mvn -f testsuite/integration exec:java -Pkeycloak-server -Dkeycloak.connectionsJpa.url='jdbc:h2:keycloak' -Dkeycloak.connectionsJpa.databaseSchema='update'
+    
+Once the server has started fully, stop it and run:
+    
+    mvn -f testsuite/integration exec:java -Pkeycloak-server -Dkeycloak.connectionsJpa.url='jdbc:h2:keycloak' -Dkeycloak.connectionsJpa.databaseSchema='development-validate'
+
+
+Creating a Mongo change-set
+---------------------------
+
+As Mongo is schema-less it's significantly easier to create a change-set. You only need to create/delete collections as
+needed, as well as update any indexes. You will also need to update existing data if required.
+ 
+Mongo change-sets are written in Java and are located in the `connections/mongo` module, to add a new change-set create 
+a new class that implements `org.keycloak.connections.mongo.updater.updates.Update` the name of the class should be 
+`Update<version>` with `.` replaced with `_`.
+
+You also need to add a reference to this file in `org.keycloak.connections.mongo.updater.DefaultMongoUpdaterProvider`. 
+It should be added last to the `DefaultMongoUpdaterProvider#updates` array.
+
+
+Testing database migration
+--------------------------
+
+Get the database from an old version of Keycloak that includes the demo applications. Start the server with this and test it. 
\ No newline at end of file
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoApplicationEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoApplicationEntity.java
index 9000d37..7534e66 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoApplicationEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoApplicationEntity.java
@@ -4,7 +4,6 @@ import com.mongodb.DBObject;
 import com.mongodb.QueryBuilder;
 import org.keycloak.connections.mongo.api.MongoCollection;
 import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
-import org.keycloak.connections.mongo.api.MongoIndex;
 import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.entities.ApplicationEntity;
 
@@ -12,7 +11,6 @@ import org.keycloak.models.entities.ApplicationEntity;
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
 @MongoCollection(collectionName = "applications")
-@MongoIndex(fields = { "realmId", "name" }, unique = true)
 public class MongoApplicationEntity extends ApplicationEntity implements MongoIdentifiableEntity {
 
     @Override
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoOAuthClientEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoOAuthClientEntity.java
index bf07f91..ab01359 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoOAuthClientEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoOAuthClientEntity.java
@@ -4,7 +4,6 @@ import com.mongodb.DBObject;
 import com.mongodb.QueryBuilder;
 import org.keycloak.connections.mongo.api.MongoCollection;
 import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
-import org.keycloak.connections.mongo.api.MongoIndex;
 import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.entities.OAuthClientEntity;
 
@@ -12,7 +11,6 @@ import org.keycloak.models.entities.OAuthClientEntity;
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
 @MongoCollection(collectionName = "oauthClients")
-@MongoIndex(fields = { "realmId", "name" }, unique = true)
 public class MongoOAuthClientEntity extends OAuthClientEntity implements MongoIdentifiableEntity {
 
     @Override
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRealmEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRealmEntity.java
index c1a4267..2784352 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRealmEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRealmEntity.java
@@ -4,7 +4,6 @@ import com.mongodb.DBObject;
 import com.mongodb.QueryBuilder;
 import org.keycloak.connections.mongo.api.MongoCollection;
 import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
-import org.keycloak.connections.mongo.api.MongoIndex;
 import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.entities.RealmEntity;
 
@@ -12,7 +11,6 @@ import org.keycloak.models.entities.RealmEntity;
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
 @MongoCollection(collectionName = "realms")
-@MongoIndex(fields = { "name" }, unique = true)
 public class MongoRealmEntity extends RealmEntity implements MongoIdentifiableEntity {
 
     @Override
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRoleEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRoleEntity.java
index be034da..cf0cfed 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRoleEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoRoleEntity.java
@@ -6,7 +6,6 @@ import org.jboss.logging.Logger;
 import org.keycloak.connections.mongo.api.MongoCollection;
 import org.keycloak.connections.mongo.api.MongoField;
 import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
-import org.keycloak.connections.mongo.api.MongoIndex;
 import org.keycloak.connections.mongo.api.MongoStore;
 import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.entities.RoleEntity;
@@ -17,7 +16,6 @@ import java.util.List;
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
 @MongoCollection(collectionName = "roles")
-@MongoIndex(fields = "nameIndex", unique = true)
 public class MongoRoleEntity extends RoleEntity implements MongoIdentifiableEntity {
 
     private static final Logger logger = Logger.getLogger(MongoRoleEntity.class);
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserEntity.java
index a0c16bf..c9f317e 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/MongoUserEntity.java
@@ -2,8 +2,6 @@ package org.keycloak.models.mongo.keycloak.entities;
 
 import org.keycloak.connections.mongo.api.MongoCollection;
 import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
-import org.keycloak.connections.mongo.api.MongoIndex;
-import org.keycloak.connections.mongo.api.MongoIndexes;
 import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
 import org.keycloak.models.entities.UserEntity;
 
@@ -11,13 +9,8 @@ import org.keycloak.models.entities.UserEntity;
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
 @MongoCollection(collectionName = "users")
-@MongoIndexes({
-        @MongoIndex(fields = { "realmId", "username" }, unique = true),
-        @MongoIndex(fields = { "emailIndex" }, unique = true, sparse = true),
-})
 public class MongoUserEntity extends UserEntity implements MongoIdentifiableEntity {
 
-
     public String getEmailIndex() {
         return getEmail() != null ? getRealmId() + "//" + getEmail() : null;
     }
diff --git a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java
index b43f827..53cf7f3 100755
--- a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java
+++ b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/MemUserSessionProvider.java
@@ -253,7 +253,7 @@ public class MemUserSessionProvider implements UserSessionProvider {
     @Override
     public void onClientRemoved(RealmModel realm, ClientModel client) {
         for (ClientSessionEntity e : clientSessions.values()) {
-            if (e.getSession().getRealm().equals(realm.getId()) && e.getClientId().equals(client.getId())) {
+            if (e.getRealmId().equals(realm.getId()) && e.getClientId().equals(client.getId())) {
                 clientSessions.remove(e.getId());
                 e.getSession().removeClientSession(e);
             }

pom.xml 6(+6 -0)

diff --git a/pom.xml b/pom.xml
index 73523c0..763bdcb 100755
--- a/pom.xml
+++ b/pom.xml
@@ -44,6 +44,7 @@
         <selenium.version>2.35.0</selenium.version>
         <javax.mail.version>1.4.5</javax.mail.version>
         <infinispan.version>6.0.2.Final</infinispan.version>
+        <liquibase.version>3.2.2</liquibase.version>
 
         <!-- maven-compiler-plugin -->
         <maven.compiler.target>1.6</maven.compiler.target>
@@ -449,6 +450,11 @@
                 <artifactId>infinispan-core</artifactId>
                 <version>${infinispan.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.liquibase</groupId>
+                <artifactId>liquibase-core</artifactId>
+                <version>${liquibase.version}</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 
diff --git a/testsuite/integration/src/main/resources/log4j.properties b/testsuite/integration/src/main/resources/log4j.properties
index 315b7ca..778d39a 100755
--- a/testsuite/integration/src/main/resources/log4j.properties
+++ b/testsuite/integration/src/main/resources/log4j.properties
@@ -6,6 +6,15 @@ log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] %m%n
 
 log4j.logger.org.keycloak=info
 
+# Enable to view loaded SPI and Providers
+# log4j.logger.org.keycloak.services.DefaultKeycloakSessionFactory=debug
+
+# Enable to view database updates
+# log4j.logger.org.keycloak.connections.jpa.updater.liquibase.LiquibaseJpaUpdaterProvider=debug
+# log4j.logger.org.keycloak.connections.mongo.updater.DefaultMongoUpdaterProvider=debug
+
+# log4j.logger.org.keycloak.connections.jpa.DefaultJpaConnectionProviderFactory=debug
+
 log4j.logger.org.xnio=off
 log4j.logger.org.hibernate=off
 log4j.logger.org.jboss.resteasy=warn
\ No newline at end of file
diff --git a/testsuite/integration/src/main/resources/META-INF/keycloak-server.json b/testsuite/integration/src/main/resources/META-INF/keycloak-server.json
index 6e90fa1..728631d 100755
--- a/testsuite/integration/src/main/resources/META-INF/keycloak-server.json
+++ b/testsuite/integration/src/main/resources/META-INF/keycloak-server.json
@@ -67,7 +67,7 @@
             "driverDialect": "${keycloak.connectionsJpa.driverDialect:}",
             "user": "${keycloak.connectionsJpa.user:sa}",
             "password": "${keycloak.connectionsJpa.password:}",
-            "databaseSchema": "${keycloak.connectionsJpa.databaseSchema:create-drop}",
+            "databaseSchema": "${keycloak.connectionsJpa.databaseSchema:update}",
             "showSql": "${keycloak.connectionsJpa.showSql:false}",
             "formatSql": "${keycloak.connectionsJpa.formatSql:true}"
         }
@@ -78,7 +78,7 @@
             "host": "${keycloak.connectionsMongo.host:127.0.0.1}",
             "port": "${keycloak.connectionsMongo.port:27017}",
             "db": "${keycloak.connectionsMongo.db:keycloak}",
-            "clearOnStartup": "${keycloak.connectionsMongo.clearOnStartup:false}"
+            "databaseSchema": "${keycloak.connectionsMongo.databaseSchema:update}"
         }
     }
 }
\ No newline at end of file
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
index 5aa52c3..4cfaaa3 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
@@ -157,15 +157,11 @@ public class AccountTest {
         });
     }
 
-    @Test
-    @Ignore
-    public void runit() throws Exception {
-        Thread.sleep(10000000);
-
-    }
-
-
-
+//    @Test
+//    @Ignore
+//    public void runit() throws Exception {
+//        Thread.sleep(10000000);
+//    }
 
     @Test
     public void returnToAppFromQueryParam() {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeImportRoleTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeImportRoleTest.java
index 32ba03c..47d1ff5 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeImportRoleTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeImportRoleTest.java
@@ -54,7 +54,7 @@ public class CompositeImportRoleTest {
         @Override
         protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
             RealmRepresentation representation = KeycloakServer.loadJson(getClass().getResourceAsStream("/testcomposite.json"), RealmRepresentation.class);
-            representation.setId("Test");
+            representation.setId("test");
             RealmModel realm = manager.importRealm(representation);
 
             realmPublicKey = realm.getPublicKey();
@@ -78,7 +78,7 @@ public class CompositeImportRoleTest {
 
     @Test
     public void testAppCompositeUser() throws Exception {
-        oauth.realm("Test");
+        oauth.realm("test");
         oauth.realmPublicKey(realmPublicKey);
         oauth.clientId("APP_COMPOSITE_APPLICATION");
         oauth.doLogin("APP_COMPOSITE_USER", "password");
@@ -92,7 +92,7 @@ public class CompositeImportRoleTest {
 
         AccessToken token = oauth.verifyToken(response.getAccessToken());
 
-        Assert.assertEquals(keycloakRule.getUser("Test", "APP_COMPOSITE_USER").getId(), token.getSubject());
+        Assert.assertEquals(keycloakRule.getUser("test", "APP_COMPOSITE_USER").getId(), token.getSubject());
 
         Assert.assertEquals(1, token.getResourceAccess("APP_ROLE_APPLICATION").getRoles().size());
         Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
@@ -103,7 +103,7 @@ public class CompositeImportRoleTest {
 
     @Test
     public void testRealmAppCompositeUser() throws Exception {
-        oauth.realm("Test");
+        oauth.realm("test");
         oauth.realmPublicKey(realmPublicKey);
         oauth.clientId("APP_ROLE_APPLICATION");
         oauth.doLogin("REALM_APP_COMPOSITE_USER", "password");
@@ -117,7 +117,7 @@ public class CompositeImportRoleTest {
 
         AccessToken token = oauth.verifyToken(response.getAccessToken());
 
-        Assert.assertEquals(keycloakRule.getUser("Test", "REALM_APP_COMPOSITE_USER").getId(), token.getSubject());
+        Assert.assertEquals(keycloakRule.getUser("test", "REALM_APP_COMPOSITE_USER").getId(), token.getSubject());
 
         Assert.assertEquals(1, token.getResourceAccess("APP_ROLE_APPLICATION").getRoles().size());
         Assert.assertTrue(token.getResourceAccess("APP_ROLE_APPLICATION").isUserInRole("APP_ROLE_1"));
@@ -127,7 +127,7 @@ public class CompositeImportRoleTest {
 
     @Test
     public void testRealmOnlyWithUserCompositeAppComposite() throws Exception {
-        oauth.realm("Test");
+        oauth.realm("test");
         oauth.realmPublicKey(realmPublicKey);
         oauth.clientId("REALM_COMPOSITE_1_APPLICATION");
         oauth.doLogin("REALM_COMPOSITE_1_USER", "password");
@@ -141,7 +141,7 @@ public class CompositeImportRoleTest {
 
         AccessToken token = oauth.verifyToken(response.getAccessToken());
 
-        Assert.assertEquals(keycloakRule.getUser("Test", "REALM_COMPOSITE_1_USER").getId(), token.getSubject());
+        Assert.assertEquals(keycloakRule.getUser("test", "REALM_COMPOSITE_1_USER").getId(), token.getSubject());
 
         Assert.assertEquals(2, token.getRealmAccess().getRoles().size());
         Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_COMPOSITE_1"));
@@ -150,7 +150,7 @@ public class CompositeImportRoleTest {
 
     @Test
     public void testRealmOnlyWithUserCompositeAppRole() throws Exception {
-        oauth.realm("Test");
+        oauth.realm("test");
         oauth.realmPublicKey(realmPublicKey);
         oauth.clientId("REALM_ROLE_1_APPLICATION");
         oauth.doLogin("REALM_COMPOSITE_1_USER", "password");
@@ -164,7 +164,7 @@ public class CompositeImportRoleTest {
 
         AccessToken token = oauth.verifyToken(response.getAccessToken());
 
-        Assert.assertEquals(keycloakRule.getUser("Test", "REALM_COMPOSITE_1_USER").getId(), token.getSubject());
+        Assert.assertEquals(keycloakRule.getUser("test", "REALM_COMPOSITE_1_USER").getId(), token.getSubject());
 
         Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
         Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
@@ -172,7 +172,7 @@ public class CompositeImportRoleTest {
 
     @Test
     public void testRealmOnlyWithUserRoleAppComposite() throws Exception {
-        oauth.realm("Test");
+        oauth.realm("test");
         oauth.realmPublicKey(realmPublicKey);
         oauth.clientId("REALM_COMPOSITE_1_APPLICATION");
         oauth.doLogin("REALM_ROLE_1_USER", "password");
@@ -186,13 +186,10 @@ public class CompositeImportRoleTest {
 
         AccessToken token = oauth.verifyToken(response.getAccessToken());
 
-        Assert.assertEquals(keycloakRule.getUser("Test", "REALM_ROLE_1_USER").getId(), token.getSubject());
+        Assert.assertEquals(keycloakRule.getUser("test", "REALM_ROLE_1_USER").getId(), token.getSubject());
 
         Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
         Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
     }
 
-
-
-
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java
index 6aaf068..b661d5a 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/composites/CompositeRoleTest.java
@@ -58,7 +58,7 @@ public class CompositeRoleTest {
     public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule(){
         @Override
         protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
-            RealmModel realm = manager.createRealm("Test");
+            RealmModel realm = manager.createRealm("test");
             KeycloakModelUtils.generateRealmKeys(realm);
             realmPublicKey = realm.getPublicKey();
             realm.setSsoSessionIdleTimeout(3000);
@@ -166,7 +166,7 @@ public class CompositeRoleTest {
 
     @Test
     public void testAppCompositeUser() throws Exception {
-        oauth.realm("Test");
+        oauth.realm("test");
         oauth.realmPublicKey(realmPublicKey);
         oauth.clientId("APP_COMPOSITE_APPLICATION");
         oauth.doLogin("APP_COMPOSITE_USER", "password");
@@ -180,7 +180,7 @@ public class CompositeRoleTest {
 
         AccessToken token = oauth.verifyToken(response.getAccessToken());
 
-        Assert.assertEquals(keycloakRule.getUser("Test", "APP_COMPOSITE_USER").getId(), token.getSubject());
+        Assert.assertEquals(keycloakRule.getUser("test", "APP_COMPOSITE_USER").getId(), token.getSubject());
 
         Assert.assertEquals(1, token.getResourceAccess("APP_ROLE_APPLICATION").getRoles().size());
         Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
@@ -194,7 +194,7 @@ public class CompositeRoleTest {
 
     @Test
     public void testRealmAppCompositeUser() throws Exception {
-        oauth.realm("Test");
+        oauth.realm("test");
         oauth.realmPublicKey(realmPublicKey);
         oauth.clientId("APP_ROLE_APPLICATION");
         oauth.doLogin("REALM_APP_COMPOSITE_USER", "password");
@@ -208,7 +208,7 @@ public class CompositeRoleTest {
 
         AccessToken token = oauth.verifyToken(response.getAccessToken());
 
-        Assert.assertEquals(keycloakRule.getUser("Test", "REALM_APP_COMPOSITE_USER").getId(), token.getSubject());
+        Assert.assertEquals(keycloakRule.getUser("test", "REALM_APP_COMPOSITE_USER").getId(), token.getSubject());
 
         Assert.assertEquals(1, token.getResourceAccess("APP_ROLE_APPLICATION").getRoles().size());
         Assert.assertTrue(token.getResourceAccess("APP_ROLE_APPLICATION").isUserInRole("APP_ROLE_1"));
@@ -219,7 +219,7 @@ public class CompositeRoleTest {
 
     @Test
     public void testRealmOnlyWithUserCompositeAppComposite() throws Exception {
-        oauth.realm("Test");
+        oauth.realm("test");
         oauth.realmPublicKey(realmPublicKey);
         oauth.clientId("REALM_COMPOSITE_1_APPLICATION");
         oauth.doLogin("REALM_COMPOSITE_1_USER", "password");
@@ -233,7 +233,7 @@ public class CompositeRoleTest {
 
         AccessToken token = oauth.verifyToken(response.getAccessToken());
 
-        Assert.assertEquals(keycloakRule.getUser("Test", "REALM_COMPOSITE_1_USER").getId(), token.getSubject());
+        Assert.assertEquals(keycloakRule.getUser("test", "REALM_COMPOSITE_1_USER").getId(), token.getSubject());
 
         Assert.assertEquals(2, token.getRealmAccess().getRoles().size());
         Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_COMPOSITE_1"));
@@ -245,7 +245,7 @@ public class CompositeRoleTest {
 
     @Test
     public void testRealmOnlyWithUserCompositeAppRole() throws Exception {
-        oauth.realm("Test");
+        oauth.realm("test");
         oauth.realmPublicKey(realmPublicKey);
         oauth.clientId("REALM_ROLE_1_APPLICATION");
         oauth.doLogin("REALM_COMPOSITE_1_USER", "password");
@@ -259,7 +259,7 @@ public class CompositeRoleTest {
 
         AccessToken token = oauth.verifyToken(response.getAccessToken());
 
-        Assert.assertEquals(keycloakRule.getUser("Test", "REALM_COMPOSITE_1_USER").getId(), token.getSubject());
+        Assert.assertEquals(keycloakRule.getUser("test", "REALM_COMPOSITE_1_USER").getId(), token.getSubject());
 
         Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
         Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
@@ -270,7 +270,7 @@ public class CompositeRoleTest {
 
     @Test
     public void testRealmOnlyWithUserRoleAppComposite() throws Exception {
-        oauth.realm("Test");
+        oauth.realm("test");
         oauth.realmPublicKey(realmPublicKey);
         oauth.clientId("REALM_COMPOSITE_1_APPLICATION");
         oauth.doLogin("REALM_ROLE_1_USER", "password");
@@ -284,7 +284,7 @@ public class CompositeRoleTest {
 
         AccessToken token = oauth.verifyToken(response.getAccessToken());
 
-        Assert.assertEquals(keycloakRule.getUser("Test", "REALM_ROLE_1_USER").getId(), token.getSubject());
+        Assert.assertEquals(keycloakRule.getUser("test", "REALM_ROLE_1_USER").getId(), token.getSubject());
 
         Assert.assertEquals(1, token.getRealmAccess().getRoles().size());
         Assert.assertTrue(token.getRealmAccess().isUserInRole("REALM_ROLE_1"));
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
index c4bf603..5261da6 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/exportimport/ExportImportTest.java
@@ -64,7 +64,7 @@ public class ExportImportTest {
                 propsHelper.pushProperty(JPA_CONNECTION_URL, "jdbc:h2:file:" + dbDir + ";DB_CLOSE_DELAY=-1");
                 connectionURLSet = true;
             }
-            propsHelper.pushProperty(JPA_DB_SCHEMA, "create");
+            propsHelper.pushProperty(JPA_DB_SCHEMA, "update");
         }
 
         @Override
@@ -98,7 +98,6 @@ public class ExportImportTest {
             addUser(manager.getSession().users(), appRealm, "user1", "password");
             addUser(manager.getSession().users(), appRealm, "user2", "password");
             addUser(manager.getSession().users(), appRealm, "user3", "password");
-            addUser(manager.getSession().users(), adminstrationRealm, "admin2", "admin2");
 
             // Import "test-realm" realm
             try {
@@ -129,6 +128,10 @@ public class ExportImportTest {
                 systemProps.remove(propToRemove);
             }
         }
+
+        protected String[] getTestRealms() {
+            return new String[]{"test", "demo", "test-realm"};
+        }
     };
 
     @ClassRule
@@ -222,10 +225,6 @@ public class ExportImportTest {
             new RealmManager(session).removeRealm(realmProvider.getRealmByName("test"));
             Assert.assertEquals(2, realmProvider.getRealms().size());
 
-            RealmModel master = realmProvider.getRealmByName(Config.getAdminRealm());
-            UserModel admin2 = session.users().getUserByUsername("admin2", master);
-            session.users().removeUser(master, admin2);
-            assertNotAuthenticated(userProvider, realmProvider, Config.getAdminRealm(), "admin2", "admin2");
             assertNotAuthenticated(userProvider, realmProvider, "test", "test-user@localhost", "password");
             assertNotAuthenticated(userProvider, realmProvider, "test", "user1", "password");
             assertNotAuthenticated(userProvider, realmProvider, "test", "user2", "password");
@@ -247,7 +246,6 @@ public class ExportImportTest {
             UserProvider userProvider = session.users();
             Assert.assertEquals(3, model.getRealms().size());
 
-            assertAuthenticated(userProvider, model, Config.getAdminRealm(), "admin2", "admin2");
             assertAuthenticated(userProvider, model, "test", "test-user@localhost", "password");
             assertAuthenticated(userProvider, model, "test", "user1", "password");
             assertAuthenticated(userProvider, model, "test", "user2", "password");
@@ -275,11 +273,6 @@ public class ExportImportTest {
             new RealmManager(session).removeRealm(realmProvider.getRealmByName("test"));
             Assert.assertEquals(2, realmProvider.getRealms().size());
 
-            RealmModel master = realmProvider.getRealmByName(Config.getAdminRealm());
-            UserModel admin2 = session.users().getUserByUsername("admin2", master);
-            session.users().removeUser(master, admin2);
-
-            assertNotAuthenticated(userProvider, realmProvider, Config.getAdminRealm(), "admin2", "admin2");
             assertNotAuthenticated(userProvider, realmProvider, "test", "test-user@localhost", "password");
             assertNotAuthenticated(userProvider, realmProvider, "test", "user1", "password");
             assertNotAuthenticated(userProvider, realmProvider, "test", "user2", "password");
@@ -301,13 +294,10 @@ public class ExportImportTest {
             UserProvider userProvider = session.users();
             Assert.assertEquals(3, realmProvider.getRealms().size());
 
-            assertNotAuthenticated(userProvider, realmProvider, Config.getAdminRealm(), "admin2", "admin2");
             assertAuthenticated(userProvider, realmProvider, "test", "test-user@localhost", "password");
             assertAuthenticated(userProvider, realmProvider, "test", "user1", "password");
             assertAuthenticated(userProvider, realmProvider, "test", "user2", "password");
             assertAuthenticated(userProvider, realmProvider, "test", "user3", "password");
-
-            addUser(userProvider, realmProvider.getRealmByName(Config.getAdminRealm()), "admin2", "admin2");
         } finally {
             keycloakRule.stopSession(session, true);
         }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
index 7b616f3..06e793f 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
@@ -30,12 +30,15 @@ import java.net.Socket;
  * @version $Revision: 1 $
  */
 public abstract class AbstractKeycloakRule extends ExternalResource {
+
     protected KeycloakServer server;
 
     protected void before() throws Throwable {
         server = new KeycloakServer();
         server.start();
 
+        removeTestRealms();
+
         setupKeycloak();
     }
 
@@ -123,6 +126,7 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
         deploymentInfo.addServlet(servlet);
         return deploymentInfo;
     }
+
     public void deployApplication(String name, String contextPath, Class<? extends Servlet> servletClass, String adapterConfigPath, String role) {
         deployApplication(name, contextPath, servletClass, adapterConfigPath, role, true);
 
@@ -147,9 +151,27 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
 
     @Override
     protected void after() {
+        removeTestRealms();
         stopServer();
     }
 
+    protected void removeTestRealms() {
+        KeycloakSession session = server.getSessionFactory().create();
+        try {
+            session.getTransaction().begin();
+            RealmManager realmManager = new RealmManager(session);
+            for (String realmName : getTestRealms()) {
+                RealmModel realm = realmManager.getRealmByName(realmName);
+                if (realm != null) {
+                    realmManager.removeRealm(realm);
+                }
+            }
+            session.getTransaction().commit();
+        } finally {
+            session.close();
+        }
+    }
+
     public RealmRepresentation loadJson(String path) throws IOException {
         InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
         ByteArrayOutputStream os = new ByteArrayOutputStream();
@@ -169,7 +191,7 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
 
     public void stopSession(KeycloakSession session, boolean commit) {
         KeycloakTransaction transaction = session.getTransaction();
-        if (commit) {
+        if (commit && !transaction.getRollbackOnly()) {
             transaction.commit();
         } else {
             transaction.rollback();
@@ -208,4 +230,9 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
             Thread.currentThread().interrupt();
         }
     }
+
+    protected String[] getTestRealms() {
+        return new String[]{"test", "demo"};
+    }
+
 }
diff --git a/testsuite/integration/src/test/resources/testcomposite.json b/testsuite/integration/src/test/resources/testcomposite.json
index 8f3f76f..5870eb0 100755
--- a/testsuite/integration/src/test/resources/testcomposite.json
+++ b/testsuite/integration/src/test/resources/testcomposite.json
@@ -1,6 +1,6 @@
 {
-    "id": "Test",
-    "realm": "Test",
+    "id": "test",
+    "realm": "test",
     "enabled": true,
     "accessTokenLifespan": 600,
     "accessCodeLifespan": 600,