keycloak-uncached

Changes

model/api/pom.xml 11(+11 -0)

model/api/src/main/java/org/keycloak/models/IdGenerator.java 15(+0 -15)

model/api/src/main/java/org/keycloak/models/utils/KeycloakSessionUtils.java 18(+0 -18)

model/jpa/pom.xml 30(+30 -0)

model/mongo/pom.xml 87(+74 -13)

model/mongo/src/main/java/org/keycloak/models/mongo/api/AbstractAttributedNoSQLObject.java 37(+0 -37)

model/mongo/src/main/java/org/keycloak/models/mongo/api/AttributedNoSQLObject.java 17(+0 -17)

model/mongo/src/main/java/org/keycloak/models/mongo/api/NoSQL.java 36(+0 -36)

model/mongo/src/main/java/org/keycloak/models/mongo/api/NoSQLId.java 18(+0 -18)

model/mongo/src/main/java/org/keycloak/models/mongo/api/query/NoSQLQuery.java 26(+0 -26)

model/mongo/src/main/java/org/keycloak/models/mongo/api/query/NoSQLQueryBuilder.java 31(+0 -31)

model/mongo/src/main/java/org/keycloak/models/mongo/api/types/TypeConverter.java 112(+0 -112)

model/mongo/src/main/java/org/keycloak/models/mongo/impl/MongoDBImpl.java 324(+0 -324)

model/mongo/src/main/java/org/keycloak/models/mongo/impl/MongoDBQueryBuilder.java 38(+0 -38)

model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBListConverter.java 73(+0 -73)

model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/ClassCache.java 37(+0 -37)

model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/NoSQLObjectConverter.java 66(+0 -66)

model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoDBSessionFactory.java 70(+0 -70)

model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/credentials/PasswordCredentialHandler.java 154(+0 -154)

model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/credentials/TOTPCredentialHandler.java 135(+0 -135)

model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/credentials/OTPData.java 66(+0 -66)

model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/credentials/PasswordData.java 66(+0 -66)

model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/RoleData.java 98(+0 -98)

model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/data/SocialLinkData.java 55(+0 -55)

model/mongo/src/main/java/org/keycloak/models/mongo/MongoDBSessionFactoryTestContext.java 57(+0 -57)

model/mongo/src/main/java/org/keycloak/models/mongo/MongoRunnerListener.java 53(+0 -53)

model/mongo/src/main/java/org/keycloak/models/mongo/PropertiesManager.java 58(+0 -58)

model/pom.xml 3(+2 -1)

model/tests/pom.xml 69(+69 -0)

pom.xml 14(+7 -7)

services/pom.xml 51(+0 -51)

services/src/main/java/org/keycloak/services/utils/PropertiesManager.java 88(+0 -88)

services/src/test/java/org/keycloak/test/AbstractKeycloakServerTest.java 70(+0 -70)

services/src/test/java/org/keycloak/test/common/AbstractKeycloakTest.java 96(+0 -96)

services/src/test/java/org/keycloak/test/common/JpaSessionFactoryTestContext.java 24(+0 -24)

services/src/test/java/org/keycloak/test/common/PicketlinkSessionFactoryTestContext.java 24(+0 -24)

services/src/test/java/org/keycloak/test/common/SessionFactoryTestContext.java 17(+0 -17)

Details

model/api/pom.xml 11(+11 -0)

diff --git a/model/api/pom.xml b/model/api/pom.xml
index 2c64294..a5260d4 100755
--- a/model/api/pom.xml
+++ b/model/api/pom.xml
@@ -18,6 +18,17 @@
             <artifactId>base64</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk16</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-core</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
             <scope>test</scope>
diff --git a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
new file mode 100644
index 0000000..4582a03
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
@@ -0,0 +1,86 @@
+package org.keycloak.models.utils;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.bouncycastle.openssl.PEMWriter;
+import org.keycloak.models.RoleModel;
+import org.keycloak.util.PemUtils;
+
+/**
+ * Set of helper methods, which are useful in various model implementations.
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public final class KeycloakModelUtils {
+
+    private KeycloakModelUtils() {
+    }
+
+    private static AtomicLong counter = new AtomicLong(1);
+
+    public static String generateId() {
+        return counter.getAndIncrement() + "-" + System.currentTimeMillis();
+    }
+
+    public static PublicKey getPublicKey(String publicKeyPem) {
+        if (publicKeyPem != null) {
+            try {
+                return PemUtils.decodePublicKey(publicKeyPem);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        } else {
+            return null;
+        }
+    }
+
+    public static PrivateKey getPrivateKey(String privateKeyPem) {
+        if (privateKeyPem != null) {
+            try {
+                return PemUtils.decodePrivateKey(privateKeyPem);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+        return null;
+    }
+
+    public static String getPemFromKey(Key key) {
+        StringWriter writer = new StringWriter();
+        PEMWriter pemWriter = new PEMWriter(writer);
+        try {
+            pemWriter.writeObject(key);
+            pemWriter.flush();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        String s = writer.toString();
+        return PemUtils.removeBeginEnd(s);
+    }
+
+    /**
+     * Deep search if given role is descendant of composite role
+     *
+     * @param role role to check
+     * @param composite composite role
+     * @param visited set of already visited roles (used for recursion)
+     * @return true if "role" is descendant of "composite"
+     */
+    public static boolean searchFor(RoleModel role, RoleModel composite, Set<RoleModel> visited) {
+        if (visited.contains(composite)) return false;
+        visited.add(composite);
+        Set<RoleModel> composites = composite.getComposites();
+        if (composites.contains(role)) return true;
+        for (RoleModel contained : composites) {
+            if (!contained.isComposite()) continue;
+            if (searchFor(role, contained, visited)) return true;
+        }
+        return false;
+    }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelProviderUtils.java b/model/api/src/main/java/org/keycloak/models/utils/ModelProviderUtils.java
new file mode 100644
index 0000000..e4f64cc
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/utils/ModelProviderUtils.java
@@ -0,0 +1,50 @@
+package org.keycloak.models.utils;
+
+import java.util.ServiceLoader;
+
+import org.keycloak.models.ModelProvider;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ModelProviderUtils {
+
+    public static final String MODEL_PROVIDER = "keycloak.model";
+    public static final String DEFAULT_MODEL_PROVIDER = "jpa";
+
+    public static Iterable<ModelProvider> getRegisteredProviders() {
+        return ServiceLoader.load(ModelProvider.class);
+    }
+
+    public static ModelProvider getConfiguredModelProvider(Iterable<ModelProvider> providers) {
+        String configuredProvider = System.getProperty(MODEL_PROVIDER);
+        ModelProvider provider = null;
+
+        if (configuredProvider != null) {
+            for (ModelProvider p : providers) {
+                if (p.getId().equals(configuredProvider)) {
+                    provider = p;
+                }
+            }
+        } else {
+            for (ModelProvider p : providers) {
+                if (provider == null) {
+                    provider = p;
+                }
+
+                if (p.getId().equals(DEFAULT_MODEL_PROVIDER)) {
+                    provider = p;
+                    break;
+                }
+            }
+        }
+
+        return provider;
+    }
+
+    public static ModelProvider getConfiguredModelProvider() {
+        return getConfiguredModelProvider(getRegisteredProviders());
+    }
+
+
+}

model/jpa/pom.xml 30(+30 -0)

diff --git a/model/jpa/pom.xml b/model/jpa/pom.xml
index 7ba6691..ecbf289 100755
--- a/model/jpa/pom.xml
+++ b/model/jpa/pom.xml
@@ -59,6 +59,20 @@
                 </exclusion>
             </exclusions>
         </dependency>
+
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-model-tests</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+            <scope>test</scope>
+        </dependency>
+
     </dependencies>
     <build>
         <plugins>
@@ -70,6 +84,22 @@
                     <target>1.6</target>
                 </configuration>
             </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>default-test</id>
+                        <configuration>
+                            <dependenciesToScan>
+                                <dependency>org.keycloak:keycloak-model-tests</dependency>
+                            </dependenciesToScan>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
         </plugins>
     </build>
 
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSession.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSession.java
index 7b7db28..a072c75 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSession.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSession.java
@@ -2,7 +2,7 @@ package org.keycloak.models.jpa;
 
 import org.keycloak.models.*;
 import org.keycloak.models.jpa.entities.*;
-import org.keycloak.models.utils.KeycloakSessionUtils;
+import org.keycloak.models.utils.KeycloakModelUtils;
 
 import javax.persistence.EntityManager;
 import javax.persistence.TypedQuery;
@@ -28,7 +28,7 @@ public class JpaKeycloakSession implements KeycloakSession {
 
     @Override
     public RealmModel createRealm(String name) {
-        return createRealm(KeycloakSessionUtils.generateId(), name);
+        return createRealm(KeycloakModelUtils.generateId(), name);
     }
 
     @Override
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index 965cbbe..467953c 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -13,6 +13,7 @@ import org.keycloak.models.jpa.entities.SocialLinkEntity;
 import org.keycloak.models.jpa.entities.UserEntity;
 import org.keycloak.models.jpa.entities.UserRoleMappingEntity;
 import org.keycloak.models.jpa.entities.UserScopeMappingEntity;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
 import org.keycloak.util.PemUtils;
 import org.keycloak.models.ApplicationModel;
@@ -187,59 +188,29 @@ public class RealmAdapter implements RealmModel {
     @Override
     public PublicKey getPublicKey() {
         if (publicKey != null) return publicKey;
-        String pem = getPublicKeyPem();
-        if (pem != null) {
-            try {
-                publicKey = PemUtils.decodePublicKey(pem);
-            } catch (Exception e) {
-                throw new RuntimeException(e);
-            }
-        }
+        publicKey = KeycloakModelUtils.getPublicKey(getPublicKeyPem());
         return publicKey;
     }
 
     @Override
     public void setPublicKey(PublicKey publicKey) {
         this.publicKey = publicKey;
-        StringWriter writer = new StringWriter();
-        PEMWriter pemWriter = new PEMWriter(writer);
-        try {
-            pemWriter.writeObject(publicKey);
-            pemWriter.flush();
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-        String s = writer.toString();
-        setPublicKeyPem(PemUtils.removeBeginEnd(s));
+        String publicKeyPem = KeycloakModelUtils.getPemFromKey(publicKey);
+        setPublicKeyPem(publicKeyPem);
     }
 
     @Override
     public PrivateKey getPrivateKey() {
         if (privateKey != null) return privateKey;
-        String pem = getPrivateKeyPem();
-        if (pem != null) {
-            try {
-                privateKey = PemUtils.decodePrivateKey(pem);
-            } catch (Exception e) {
-                throw new RuntimeException(e);
-            }
-        }
+        privateKey = KeycloakModelUtils.getPrivateKey(getPrivateKeyPem());
         return privateKey;
     }
 
     @Override
     public void setPrivateKey(PrivateKey privateKey) {
         this.privateKey = privateKey;
-        StringWriter writer = new StringWriter();
-        PEMWriter pemWriter = new PEMWriter(writer);
-        try {
-            pemWriter.writeObject(privateKey);
-            pemWriter.flush();
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-        String s = writer.toString();
-        setPrivateKeyPem(PemUtils.removeBeginEnd(s));
+        String privateKeyPem = KeycloakModelUtils.getPemFromKey(privateKey);
+        setPrivateKeyPem(privateKeyPem);
     }
 
     protected RequiredCredentialModel initRequiredCredentialModel(String type) {
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java
index 2c9a8c9..e418441 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java
@@ -6,6 +6,7 @@ import org.keycloak.models.RoleModel;
 import org.keycloak.models.jpa.entities.ApplicationRoleEntity;
 import org.keycloak.models.jpa.entities.RealmRoleEntity;
 import org.keycloak.models.jpa.entities.RoleEntity;
+import org.keycloak.models.utils.KeycloakModelUtils;
 
 import javax.persistence.EntityManager;
 import java.util.HashSet;
@@ -94,27 +95,13 @@ public class RoleAdapter implements RoleModel {
         return set;
     }
 
-    public static boolean searchFor(RoleModel role, RoleModel composite, Set<RoleModel> visited) {
-        if (visited.contains(composite)) return false;
-        visited.add(composite);
-        Set<RoleModel> composites = composite.getComposites();
-        if (composites.contains(role)) return true;
-        for (RoleModel contained : composites) {
-            if (!contained.isComposite()) continue;
-            if (searchFor(role, contained, visited)) return true;
-        }
-        return false;
-    }
-
-
-
     @Override
     public boolean hasRole(RoleModel role) {
         if (this.equals(role)) return true;
         if (!isComposite()) return false;
 
         Set<RoleModel> visited = new HashSet<RoleModel>();
-        return searchFor(role, this, visited);
+        return KeycloakModelUtils.searchFor(role, this, visited);
     }
 
     @Override

model/mongo/pom.xml 87(+74 -13)

diff --git a/model/mongo/pom.xml b/model/mongo/pom.xml
index 537112a..fadcc7f 100755
--- a/model/mongo/pom.xml
+++ b/model/mongo/pom.xml
@@ -5,7 +5,7 @@
     <parent>
         <artifactId>keycloak-parent</artifactId>
         <groupId>org.keycloak</groupId>
-        <version>1.0-alpha-1</version>
+        <version>1.0-alpha-2-SNAPSHOT</version>
         <relativePath>../../pom.xml</relativePath>
     </parent>
     <modelVersion>4.0.0</modelVersion>
@@ -42,26 +42,27 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.picketlink</groupId>
-            <artifactId>picketlink-idm-api</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
             <groupId>org.mongodb</groupId>
             <artifactId>mongo-java-driver</artifactId>
             <scope>provided</scope>
         </dependency>
+
         <dependency>
-            <groupId>de.flapdoodle.embed</groupId>
-            <artifactId>de.flapdoodle.embed.mongo</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-model-tests</artifactId>
+            <version>${project.version}</version>
+            <classifier>tests</classifier>
             <scope>test</scope>
         </dependency>
     </dependencies>
+
+    <properties>
+        <keycloak.mongo.host>localhost</keycloak.mongo.host>
+        <keycloak.mongo.port>27018</keycloak.mongo.port>
+        <keycloak.mongo.db>keycloak</keycloak.mongo.db>
+        <keycloak.mongo.clearOnStartup>true</keycloak.mongo.clearOnStartup>
+    </properties>
+
     <build>
         <plugins>
             <plugin>
@@ -72,6 +73,66 @@
                     <target>1.6</target>
                 </configuration>
             </plugin>
+
+            <!-- Postpone tests to "integration-test" phase, so that we can bootstrap embedded mongo on 27018 before running tests -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>test</id>
+                        <phase>integration-test</phase>
+                        <goals>
+                            <goal>test</goal>
+                        </goals>
+                        <configuration>
+                            <systemPropertyVariables>
+                                <keycloak.mongo.host>${keycloak.mongo.host}</keycloak.mongo.host>
+                                <keycloak.mongo.port>${keycloak.mongo.port}</keycloak.mongo.port>
+                                <keycloak.mongo.db>${keycloak.mongo.db}</keycloak.mongo.db>
+                                <keycloak.mongo.clearOnStartup>${keycloak.mongo.clearOnStartup}</keycloak.mongo.clearOnStartup>
+                            </systemPropertyVariables>
+                            <dependenciesToScan>
+                                <dependency>org.keycloak:keycloak-model-tests</dependency>
+                            </dependenciesToScan>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>default-test</id>
+                        <configuration>
+                            <skip>true</skip>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <!-- Embedded mongo -->
+            <plugin>
+                <groupId>com.github.joelittlejohn.embedmongo</groupId>
+                <artifactId>embedmongo-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>start-mongodb</id>
+                        <phase>pre-integration-test</phase>
+                        <goals>
+                            <goal>start</goal>
+                        </goals>
+                        <configuration>
+                            <port>${keycloak.mongo.port}</port>
+                            <logging>file</logging>
+                            <logFile>${project.build.directory}/mongodb.log</logFile>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>stop-mongodb</id>
+                        <phase>post-integration-test</phase>
+                        <goals>
+                            <goal>stop</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
         </plugins>
     </build>
 </project>
\ No newline at end of file
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/api/AbstractMongoIdentifiableEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/api/AbstractMongoIdentifiableEntity.java
new file mode 100644
index 0000000..86e6e2e
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/api/AbstractMongoIdentifiableEntity.java
@@ -0,0 +1,50 @@
+package org.keycloak.models.mongo.api;
+
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class AbstractMongoIdentifiableEntity implements MongoIdentifiableEntity {
+
+    private String id;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    @Override
+    public void afterRemove(MongoStoreInvocationContext invocationContext) {
+        // Empty by default
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) return true;
+
+        if (this.id == null) return false;
+
+        if (o == null || getClass() != o.getClass()) return false;
+
+        AbstractMongoIdentifiableEntity that = (AbstractMongoIdentifiableEntity) o;
+
+        if (!getId().equals(that.getId())) return false;
+
+        return true;
+
+    }
+
+    @Override
+    public int hashCode() {
+        return id!=null ? id.hashCode() : super.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s [ id=%s ]", getClass().getSimpleName(), getId());
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/api/context/MongoStoreInvocationContext.java b/model/mongo/src/main/java/org/keycloak/models/mongo/api/context/MongoStoreInvocationContext.java
new file mode 100644
index 0000000..358f445
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/api/context/MongoStoreInvocationContext.java
@@ -0,0 +1,32 @@
+package org.keycloak.models.mongo.api.context;
+
+import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
+import org.keycloak.models.mongo.api.MongoStore;
+
+/**
+ * Context, which provides callback methods to be invoked by MongoStore
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface MongoStoreInvocationContext {
+
+    void addCreatedObject(MongoIdentifiableEntity entity);
+
+    void addLoadedObject(MongoIdentifiableEntity entity);
+
+    <T extends MongoIdentifiableEntity> T getLoadedObject(Class<T> type, String id);
+
+    void addUpdateTask(MongoIdentifiableEntity entityToUpdate, MongoTask task);
+
+    void addRemovedObject(MongoIdentifiableEntity entityToRemove);
+
+    void beforeDBSearch(Class<? extends MongoIdentifiableEntity> entityType);
+
+    void begin();
+
+    void commit();
+
+    void rollback();
+
+    MongoStore getMongoStore();
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/api/context/MongoTask.java b/model/mongo/src/main/java/org/keycloak/models/mongo/api/context/MongoTask.java
new file mode 100644
index 0000000..0c1d500
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/api/context/MongoTask.java
@@ -0,0 +1,13 @@
+package org.keycloak.models.mongo.api.context;
+
+import org.keycloak.models.mongo.api.MongoStore;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface MongoTask {
+
+    void execute();
+
+    boolean isFullUpdate();
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/api/MongoStore.java b/model/mongo/src/main/java/org/keycloak/models/mongo/api/MongoStore.java
new file mode 100755
index 0000000..9da25e5
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/api/MongoStore.java
@@ -0,0 +1,43 @@
+package org.keycloak.models.mongo.api;
+
+import com.mongodb.DBObject;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public interface MongoStore {
+
+    /**
+     * Insert new entity
+     *
+     * @param entity to insert
+     */
+    void insertEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context);
+
+    /**
+     * Update existing entity
+     *
+     * @param entity to update
+     */
+    void updateEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context);
+
+
+    <T extends MongoIdentifiableEntity> T loadEntity(Class<T> type, String id, MongoStoreInvocationContext context);
+
+    <T extends MongoIdentifiableEntity> T loadSingleEntity(Class<T> type, DBObject query, MongoStoreInvocationContext context);
+
+    <T extends MongoIdentifiableEntity> List<T> loadEntities(Class<T> type, DBObject query, MongoStoreInvocationContext context);
+
+    boolean removeEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context);
+
+    boolean removeEntity(Class<? extends MongoIdentifiableEntity> type, String id, MongoStoreInvocationContext context);
+
+    boolean removeEntities(Class<? extends MongoIdentifiableEntity> type, DBObject query, MongoStoreInvocationContext context);
+
+    <S> boolean pushItemToList(MongoIdentifiableEntity entity, String listPropertyName, S itemToPush, boolean skipIfAlreadyPresent, MongoStoreInvocationContext context);
+
+    <S> boolean pullItemFromList(MongoIdentifiableEntity entity, String listPropertyName, S itemToPull, MongoStoreInvocationContext context);
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/api/types/MapperContext.java b/model/mongo/src/main/java/org/keycloak/models/mongo/api/types/MapperContext.java
new file mode 100644
index 0000000..987f18b
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/api/types/MapperContext.java
@@ -0,0 +1,36 @@
+package org.keycloak.models.mongo.api.types;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MapperContext<T, S> {
+
+    // object to convert
+    private final T objectToConvert;
+
+    // expected return type, which could be useful information in some converters, so they are able to dynamically instantiate types
+    private final Class<? extends S> expectedReturnType;
+
+    // in case that expected return type is generic type (like "List<String>"), then genericTypes could contain list of expected generic arguments
+    private final List<Class<?>> genericTypes;
+
+    public MapperContext(T objectToConvert, Class<? extends S> expectedReturnType, List<Class<?>> genericTypes) {
+        this.objectToConvert = objectToConvert;
+        this.expectedReturnType = expectedReturnType;
+        this.genericTypes = genericTypes;
+    }
+
+    public T getObjectToConvert() {
+        return objectToConvert;
+    }
+
+    public Class<? extends S> getExpectedReturnType() {
+        return expectedReturnType;
+    }
+
+    public List<Class<?>> getGenericTypes() {
+        return genericTypes;
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/api/types/MapperRegistry.java b/model/mongo/src/main/java/org/keycloak/models/mongo/api/types/MapperRegistry.java
new file mode 100755
index 0000000..2c1c270
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/api/types/MapperRegistry.java
@@ -0,0 +1,111 @@
+package org.keycloak.models.mongo.api.types;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Registry of mappers, which allow to convert application object to database objects. MapperRegistry is main entry point to be used by application.
+ * Application can create instance of MapperRegistry and then register required Mapper objects.
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MapperRegistry {
+
+    // TODO: Thread-safety support (maybe...)
+    // Mappers of Application objects to DB objects
+    private Map<Class<?>, Mapper<?, ?>> appObjectMappers = new HashMap<Class<?>, Mapper<?, ?>>();
+
+    // Mappers of DB objects to Application objects
+    private Map<Class<?>, Map<Class<?>, Mapper<?, ?>>> dbObjectMappers = new HashMap<Class<?>, Map<Class<?>, Mapper<?,?>>>();
+
+
+    /**
+     * Add mapper for converting application objects to DB objects
+     *
+     * @param mapper
+     */
+    public void addAppObjectMapper(Mapper<?, ?> mapper) {
+        appObjectMappers.put(mapper.getTypeOfObjectToConvert(), mapper);
+    }
+
+
+    /**
+     * Add mapper for converting DB objects to application objects
+     *
+     * @param mapper
+     */
+    public void addDBObjectMapper(Mapper<?, ?> mapper) {
+        Class<?> dbObjectType = mapper.getTypeOfObjectToConvert();
+        Class<?> appObjectType = mapper.getExpectedReturnType();
+        Map<Class<?>, Mapper<?, ?>> appObjects = dbObjectMappers.get(dbObjectType);
+        if (appObjects == null) {
+            appObjects = new HashMap<Class<?>, Mapper<?, ?>>();
+            dbObjectMappers.put(dbObjectType, appObjects);
+        }
+        appObjects.put(appObjectType, mapper);
+    }
+
+
+    public <S> S convertDBObjectToApplicationObject(MapperContext<Object, S> context) {
+        Object dbObject = context.getObjectToConvert();
+        Class<?> expectedApplicationObjectType = context.getExpectedReturnType();
+
+        Class<?> dbObjectType = dbObject.getClass();
+        Mapper<Object, S> mapper;
+
+        Map<Class<?>, Mapper<?, ?>> appObjects = dbObjectMappers.get(dbObjectType);
+        if (appObjects == null) {
+            throw new IllegalArgumentException("Not found any mappers for type " + dbObjectType);
+        } else {
+            if (appObjects.size() == 1) {
+                mapper = (Mapper<Object, S>)appObjects.values().iterator().next();
+            } else {
+                // Try to find converter for requested application type
+                mapper = (Mapper<Object, S>)getAppConverterForType(context.getExpectedReturnType(), appObjects);
+            }
+        }
+
+        if (mapper == null) {
+            throw new IllegalArgumentException("Can't found mapper for type " + dbObjectType + " and expectedApplicationType " + expectedApplicationObjectType);
+        }
+
+        return mapper.convertObject(context);
+    }
+
+
+    public <S> S convertApplicationObjectToDBObject(Object applicationObject, Class<S> expectedDBObjectType) {
+        Class<?> appObjectType = applicationObject.getClass();
+        Mapper<Object, S> mapper = (Mapper<Object, S>)getAppConverterForType(appObjectType, appObjectMappers);
+        if (mapper == null) {
+            throw new IllegalArgumentException("Can't found converter for type " + appObjectType + " in registered appObjectMappers");
+        }
+        if (!expectedDBObjectType.isAssignableFrom(mapper.getExpectedReturnType())) {
+            throw new IllegalArgumentException("Converter " + mapper + " has return type " + mapper.getExpectedReturnType() +
+                    " but we need type " + expectedDBObjectType);
+        }
+        return mapper.convertObject(new MapperContext(applicationObject, expectedDBObjectType, null));
+    }
+
+    // Try to find converter for given type or all it's supertypes
+    private static Mapper<Object, ?> getAppConverterForType(Class<?> appObjectType, Map<Class<?>, Mapper<?, ?>> appObjectConverters) {
+        Mapper<Object, ?> mapper = (Mapper<Object, ?>)appObjectConverters.get(appObjectType);
+        if (mapper != null) {
+            return mapper;
+        } else {
+            Class<?>[] interfaces = appObjectType.getInterfaces();
+            for (Class<?> interface1 : interfaces) {
+                mapper = getAppConverterForType(interface1, appObjectConverters);
+                if (mapper != null) {
+                    return mapper;
+                }
+            }
+
+            Class<?> superType = appObjectType.getSuperclass();
+            if (superType != null) {
+                return getAppConverterForType(superType, appObjectConverters);
+            } else {
+                return null;
+            }
+        }
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/context/SimpleMongoStoreInvocationContext.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/context/SimpleMongoStoreInvocationContext.java
new file mode 100644
index 0000000..b0c871d
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/context/SimpleMongoStoreInvocationContext.java
@@ -0,0 +1,65 @@
+package org.keycloak.models.mongo.impl.context;
+
+import org.keycloak.models.mongo.api.MongoEntity;
+import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
+import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.mongo.api.context.MongoTask;
+
+/**
+ * Context, which is not doing any postponing of tasks and does not cache anything
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class SimpleMongoStoreInvocationContext implements MongoStoreInvocationContext {
+
+    private final MongoStore mongoStore;
+
+    public SimpleMongoStoreInvocationContext(MongoStore mongoStore) {
+        this.mongoStore = mongoStore;
+    }
+
+    @Override
+    public void addCreatedObject(MongoIdentifiableEntity entity) {
+    }
+
+    @Override
+    public void addLoadedObject(MongoIdentifiableEntity entity) {
+    }
+
+    @Override
+    public <T extends MongoIdentifiableEntity> T getLoadedObject(Class<T> type, String id) {
+        return null;
+    }
+
+    @Override
+    public void addUpdateTask(MongoIdentifiableEntity entityToUpdate, MongoTask task) {
+        task.execute();
+    }
+
+    @Override
+    public void addRemovedObject(MongoIdentifiableEntity entityToRemove) {
+        entityToRemove.afterRemove(this);
+    }
+
+    @Override
+    public void beforeDBSearch(Class<? extends MongoIdentifiableEntity> entityType) {
+    }
+
+    @Override
+    public void begin() {
+    }
+
+    @Override
+    public void commit() {
+    }
+
+    @Override
+    public void rollback() {
+    }
+
+    @Override
+    public MongoStore getMongoStore() {
+        return mongoStore;
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/context/TransactionMongoStoreInvocationContext.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/context/TransactionMongoStoreInvocationContext.java
new file mode 100644
index 0000000..5d5ff08
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/context/TransactionMongoStoreInvocationContext.java
@@ -0,0 +1,140 @@
+package org.keycloak.models.mongo.impl.context;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
+import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.mongo.api.context.MongoTask;
+
+/**
+ * Invocation context, which has some very basic support for transactions, and is able to cache loaded objects.
+ * It always execute all pending update tasks before start searching for other objects
+ *
+ * It's per-request object (not thread safe)
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class TransactionMongoStoreInvocationContext implements MongoStoreInvocationContext {
+
+    // Assumption is that all objects has unique ID (unique across all the types)
+    private Map<String, MongoIdentifiableEntity> loadedObjects = new HashMap<String, MongoIdentifiableEntity>();
+
+    private Map<MongoIdentifiableEntity, Set<MongoTask>> pendingUpdateTasks = new HashMap<MongoIdentifiableEntity, Set<MongoTask>>();
+
+    private final MongoStore mongoStore;
+
+    public TransactionMongoStoreInvocationContext(MongoStore mongoStore) {
+        this.mongoStore = mongoStore;
+    }
+
+    @Override
+    public void addCreatedObject(MongoIdentifiableEntity entity) {
+        // For now just add it to list of loaded objects
+        addLoadedObject(entity);
+    }
+
+    @Override
+    public void addLoadedObject(MongoIdentifiableEntity entity) {
+        loadedObjects.put(entity.getId(), entity);
+    }
+
+    @Override
+    public <T extends MongoIdentifiableEntity> T getLoadedObject(Class<T> type, String id) {
+        return (T)loadedObjects.get(id);
+    }
+
+    @Override
+    public void addUpdateTask(MongoIdentifiableEntity entityToUpdate, MongoTask task) {
+        if (!loadedObjects.containsValue(entityToUpdate)) {
+            throw new IllegalStateException("Entity " + entityToUpdate + " not found in loaded objects");
+        }
+
+        Set<MongoTask> currentObjectTasks = pendingUpdateTasks.get(entityToUpdate);
+        if (currentObjectTasks == null) {
+            currentObjectTasks = new LinkedHashSet<MongoTask>();
+            pendingUpdateTasks.put(entityToUpdate, currentObjectTasks);
+        } else {
+            // if task is full update, then remove all other tasks as we need to do full update of object anyway
+            if (task.isFullUpdate()) {
+                currentObjectTasks.clear();
+            } else {
+                // If it already contains task for fullUpdate, then we don't need to add ours as we need to do full update of object anyway
+                for (MongoTask current : currentObjectTasks) {
+                     if (current.isFullUpdate()) {
+                         return;
+                     }
+                }
+            }
+        }
+
+        currentObjectTasks.add(task);
+    }
+
+    @Override
+    public void addRemovedObject(MongoIdentifiableEntity entityToRemove) {
+        // Remove all pending tasks and object from cache
+        pendingUpdateTasks.remove(entityToRemove);
+        loadedObjects.remove(entityToRemove.getId());
+
+        entityToRemove.afterRemove(this);
+    }
+
+    @Override
+    public void beforeDBSearch(Class<? extends MongoIdentifiableEntity> entityType) {
+        // Now execute pending update tasks of type, which will be searched
+        Set<MongoIdentifiableEntity> toRemove = new HashSet<MongoIdentifiableEntity>();
+
+        for (MongoIdentifiableEntity currentEntity : pendingUpdateTasks.keySet()) {
+            if (currentEntity.getClass().equals(entityType)) {
+                Set<MongoTask> mongoTasks = pendingUpdateTasks.get(currentEntity);
+                for (MongoTask currentTask : mongoTasks) {
+                    currentTask.execute();
+                }
+
+                toRemove.add(currentEntity);
+            }
+        }
+
+        // Now remove all done tasks
+        for (MongoIdentifiableEntity entity : toRemove) {
+            pendingUpdateTasks.remove(entity);
+        }
+    }
+
+    @Override
+    public void begin() {
+        loadedObjects.clear();
+        pendingUpdateTasks.clear();
+    }
+
+    @Override
+    public void commit() {
+        // Now execute all pending update tasks
+        for (Set<MongoTask> mongoTasks : pendingUpdateTasks.values()) {
+            for (MongoTask currentTask : mongoTasks) {
+                currentTask.execute();
+            }
+        }
+
+        // And clear it
+        loadedObjects.clear();
+        pendingUpdateTasks.clear();
+    }
+
+    @Override
+    public void rollback() {
+        // Just clear the map without executions of tasks TODO: Attempt to do complete rollback (removal of created objects, restoring of removed objects, rollback of updates)
+        loadedObjects.clear();
+        pendingUpdateTasks.clear();
+    }
+
+    @Override
+    public MongoStore getMongoStore() {
+        return mongoStore;
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/MongoStoreImpl.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/MongoStoreImpl.java
new file mode 100755
index 0000000..0bbc094
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/MongoStoreImpl.java
@@ -0,0 +1,420 @@
+package org.keycloak.models.mongo.impl;
+
+import com.mongodb.BasicDBList;
+import com.mongodb.BasicDBObject;
+import com.mongodb.DB;
+import com.mongodb.DBCollection;
+import com.mongodb.DBCursor;
+import com.mongodb.DBObject;
+import org.bson.types.ObjectId;
+import org.jboss.logging.Logger;
+import org.keycloak.models.mongo.api.MongoCollection;
+import org.keycloak.models.mongo.api.MongoEntity;
+import org.keycloak.models.mongo.api.MongoField;
+import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
+import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.mongo.api.context.MongoTask;
+import org.keycloak.models.mongo.api.types.Mapper;
+import org.keycloak.models.mongo.api.types.MapperContext;
+import org.keycloak.models.mongo.api.types.MapperRegistry;
+import org.keycloak.models.mongo.impl.types.BasicDBListMapper;
+import org.keycloak.models.mongo.impl.types.BasicDBObjectMapper;
+import org.keycloak.models.mongo.impl.types.BasicDBObjectToMapMapper;
+import org.keycloak.models.mongo.impl.types.EnumToStringMapper;
+import org.keycloak.models.mongo.impl.types.ListMapper;
+import org.keycloak.models.mongo.impl.types.MapMapper;
+import org.keycloak.models.mongo.impl.types.MongoEntityMapper;
+import org.keycloak.models.mongo.impl.types.SimpleMapper;
+import org.keycloak.models.mongo.impl.types.StringToEnumMapper;
+import org.picketlink.common.properties.Property;
+import org.picketlink.common.properties.query.AnnotatedPropertyCriteria;
+import org.picketlink.common.properties.query.PropertyQueries;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MongoStoreImpl implements MongoStore {
+
+    private static final Class<?>[] SIMPLE_TYPES = { String.class, Integer.class, Boolean.class, Long.class, Double.class, Character.class, Date.class, byte[].class };
+
+    private final DB database;
+    private static final Logger logger = Logger.getLogger(MongoStoreImpl.class);
+
+    private final MapperRegistry mapperRegistry;
+    private ConcurrentMap<Class<? extends MongoEntity>, EntityInfo> entityInfoCache =
+            new ConcurrentHashMap<Class<? extends MongoEntity>, EntityInfo>();
+
+
+    public MongoStoreImpl(DB database, boolean clearCollectionsOnStartup, Class<? extends MongoEntity>[] managedEntityTypes) {
+        this.database = database;
+
+        mapperRegistry = new MapperRegistry();
+
+        for (Class<?> simpleConverterClass : SIMPLE_TYPES) {
+            SimpleMapper converter = new SimpleMapper(simpleConverterClass);
+            mapperRegistry.addAppObjectMapper(converter);
+            mapperRegistry.addDBObjectMapper(converter);
+        }
+
+        // Specific converter for ArrayList is added just for performance purposes to avoid recursive converter lookup (most of list impl will be ArrayList)
+        mapperRegistry.addAppObjectMapper(new ListMapper(mapperRegistry, ArrayList.class));
+        mapperRegistry.addAppObjectMapper(new ListMapper(mapperRegistry, List.class));
+        mapperRegistry.addDBObjectMapper(new BasicDBListMapper(mapperRegistry));
+
+        mapperRegistry.addAppObjectMapper(new MapMapper(HashMap.class));
+        mapperRegistry.addAppObjectMapper(new MapMapper(Map.class));
+        mapperRegistry.addDBObjectMapper(new BasicDBObjectToMapMapper());
+
+        // Enum converters
+        mapperRegistry.addAppObjectMapper(new EnumToStringMapper());
+        mapperRegistry.addDBObjectMapper(new StringToEnumMapper());
+
+        for (Class<? extends MongoEntity> type : managedEntityTypes) {
+            getEntityInfo(type);
+            mapperRegistry.addAppObjectMapper(new MongoEntityMapper(this, mapperRegistry, type));
+            mapperRegistry.addDBObjectMapper(new BasicDBObjectMapper(this, mapperRegistry, type));
+        }
+
+        if (clearCollectionsOnStartup) {
+            // dropDatabase();
+            clearManagedCollections(managedEntityTypes);
+        }
+    }
+
+    protected void dropDatabase() {
+        this.database.dropDatabase();
+        logger.info("Database " + this.database.getName() + " dropped in MongoDB");
+    }
+
+    // Don't drop database, but just clear all data in managed collections (useful for development)
+    protected void clearManagedCollections(Class<? extends MongoEntity>[] managedEntityTypes) {
+        for (Class<? extends MongoEntity> clazz : managedEntityTypes) {
+            DBCollection dbCollection = getDBCollectionForType(clazz);
+            if (dbCollection != null) {
+                dbCollection.remove(new BasicDBObject());
+                logger.debug("Collection " + dbCollection.getName() + " cleared from " + this.database.getName());
+            }
+        }
+    }
+
+    @Override
+    public void insertEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context) {
+        Class<? extends MongoEntity> clazz = entity.getClass();
+
+        // Find annotations for ID, for all the properties and for the name of the collection.
+        EntityInfo entityInfo = getEntityInfo(clazz);
+
+        // Create instance of BasicDBObject and add all declared properties to it (properties with null value probably should be skipped)
+        BasicDBObject dbObject = mapperRegistry.convertApplicationObjectToDBObject(entity, BasicDBObject.class);
+
+        DBCollection dbCollection = database.getCollection(entityInfo.getDbCollectionName());
+
+        String currentId = entity.getId();
+
+        // Inserting object, which already has oid property set. So we need to set "_id"
+        if (currentId != null) {
+            dbObject.put("_id", getObjectId(currentId));
+        }
+
+        dbCollection.insert(dbObject);
+
+        // Add id to value of given object
+        if (currentId == null) {
+            entity.setId(dbObject.getString("_id"));
+        }
+
+        // Treat object as if it is read (It is already submited to transaction)
+        context.addLoadedObject(entity);
+    }
+
+    @Override
+    public void updateEntity(final MongoIdentifiableEntity entity, MongoStoreInvocationContext context) {
+        MongoTask fullUpdateTask = new MongoTask() {
+
+            @Override
+            public void execute() {
+                Class<? extends MongoEntity> clazz = entity.getClass();
+                EntityInfo entityInfo = getEntityInfo(clazz);
+                BasicDBObject dbObject = mapperRegistry.convertApplicationObjectToDBObject(entity, BasicDBObject.class);
+                DBCollection dbCollection = database.getCollection(entityInfo.getDbCollectionName());
+
+                String currentId = entity.getId();
+
+                if (currentId == null) {
+                    throw new IllegalStateException("Can't update entity without id: " + entity);
+                } else {
+                    BasicDBObject query = new BasicDBObject("_id", getObjectId(currentId));
+                    dbCollection.update(query, dbObject);
+                }
+            }
+
+            @Override
+            public boolean isFullUpdate() {
+                return true;
+            }
+        };
+
+        // update is just added to context and postponed
+        context.addUpdateTask(entity, fullUpdateTask);
+    }
+
+
+    @Override
+    public <T extends MongoIdentifiableEntity> T loadEntity(Class<T> type, String id, MongoStoreInvocationContext context) {
+        // First look if we already read the object with this oid and type during this transaction. If yes, use it instead of DB lookup
+        T cached = context.getLoadedObject(type, id);
+        if (cached != null) return cached;
+
+        DBCollection dbCollection = getDBCollectionForType(type);
+
+        BasicDBObject idQuery = new BasicDBObject("_id", getObjectId(id));
+        DBObject dbObject = dbCollection.findOne(idQuery);
+
+        if (dbObject == null) return null;
+
+        MapperContext<Object, T> mapperContext = new MapperContext<Object, T>(dbObject, type, null);
+        T converted = mapperRegistry.convertDBObjectToApplicationObject(mapperContext);
+
+        // Now add it to loaded objects
+        context.addLoadedObject(converted);
+
+        return converted;
+    }
+
+
+    @Override
+    public <T extends MongoIdentifiableEntity> T loadSingleEntity(Class<T> type, DBObject query, MongoStoreInvocationContext context) {
+        List<T> result = loadEntities(type, query, context);
+        if (result.size() > 1) {
+            throw new IllegalStateException("There are " + result.size() + " results for type=" + type + ", query=" + query + ". We expect just one");
+        } else if (result.size() == 1) {
+            return result.get(0);
+        } else {
+            // 0 results
+            return null;
+        }
+    }
+
+
+    @Override
+    public <T extends MongoIdentifiableEntity> List<T> loadEntities(Class<T> type, DBObject query, MongoStoreInvocationContext context) {
+        // First we should execute all pending tasks before searching DB
+        context.beforeDBSearch(type);
+
+        DBCollection dbCollection = getDBCollectionForType(type);
+        DBCursor cursor = dbCollection.find(query);
+
+        return convertCursor(type, cursor, context);
+    }
+
+
+    @Override
+    public boolean removeEntity(MongoIdentifiableEntity entity, MongoStoreInvocationContext context) {
+        return removeEntity(entity.getClass(), entity.getId(), context);
+    }
+
+
+    @Override
+    public boolean removeEntity(Class<? extends MongoIdentifiableEntity> type, String id, MongoStoreInvocationContext context) {
+        MongoIdentifiableEntity found = loadEntity(type, id, context);
+        if (found == null) {
+            return false;
+        } else {
+            DBCollection dbCollection = getDBCollectionForType(type);
+            BasicDBObject dbQuery = new BasicDBObject("_id", getObjectId(id));
+            dbCollection.remove(dbQuery);
+            logger.info("Entity of type: " + type + ", id: " + id + " removed from MongoDB.");
+
+            context.addRemovedObject(found);
+            return true;
+        }
+    }
+
+
+    @Override
+    public boolean removeEntities(Class<? extends MongoIdentifiableEntity> type, DBObject query, MongoStoreInvocationContext context) {
+        List<? extends MongoIdentifiableEntity> foundObjects = loadEntities(type, query, context);
+        if (foundObjects.size() == 0) {
+            return false;
+        } else {
+            DBCollection dbCollection = getDBCollectionForType(type);
+            dbCollection.remove(query);
+            logger.info("Removed " + foundObjects.size() + " entities of type: " + type + ", query: " + query);
+
+            for (MongoIdentifiableEntity found : foundObjects) {
+                context.addRemovedObject(found);;
+            }
+            return true;
+        }
+    }
+
+    @Override
+    public <S> boolean pushItemToList(final MongoIdentifiableEntity entity, final String listPropertyName, S itemToPush, boolean skipIfAlreadyPresent, MongoStoreInvocationContext context) {
+        final Class<? extends MongoEntity> type = entity.getClass();
+        EntityInfo entityInfo = getEntityInfo(type);
+
+        // Add item to list directly in this object
+        Property<Object> listProperty = entityInfo.getPropertyByName(listPropertyName);
+        if (listProperty == null) {
+            throw new IllegalArgumentException("Property " + listPropertyName + " doesn't exist on object " + entity);
+        }
+
+        List<S> list = (List<S>)listProperty.getValue(entity);
+        if (list == null) {
+            list = new ArrayList<S>();
+            listProperty.setValue(entity, list);
+        }
+
+        // Skip if item is already in list
+        if (skipIfAlreadyPresent && list.contains(itemToPush)) {
+            return false;
+        }
+
+        // Update java object
+        list.add(itemToPush);
+
+        // Add update of list to pending tasks
+        final List<S> listt = list;
+        context.addUpdateTask(entity, new MongoTask() {
+
+            @Override
+            public void execute() {
+                // Now DB update of new list with usage of $set
+                BasicDBList dbList = mapperRegistry.convertApplicationObjectToDBObject(listt, BasicDBList.class);
+
+                BasicDBObject query = new BasicDBObject("_id", getObjectId(entity.getId()));
+                BasicDBObject listObject = new BasicDBObject(listPropertyName, dbList);
+                BasicDBObject setCommand = new BasicDBObject("$set", listObject);
+                getDBCollectionForType(type).update(query, setCommand);
+            }
+
+            @Override
+            public boolean isFullUpdate() {
+                return false;
+            }
+        });
+
+        return true;
+    }
+
+
+    @Override
+    public <S> boolean pullItemFromList(final MongoIdentifiableEntity entity, final String listPropertyName, final S itemToPull, MongoStoreInvocationContext context) {
+        final Class<? extends MongoEntity> type = entity.getClass();
+        EntityInfo entityInfo = getEntityInfo(type);
+
+        // Remove item from list directly in this object
+        Property<Object> listProperty = entityInfo.getPropertyByName(listPropertyName);
+        if (listProperty == null) {
+            throw new IllegalArgumentException("Property " + listPropertyName + " doesn't exist on object " + entity);
+        }
+        List<S> list = (List<S>)listProperty.getValue(entity);
+
+        // If list is null, we skip both object and DB update
+        if (list == null || !list.contains(itemToPull)) {
+            return false;
+        } else {
+
+            // Update java object
+            list.remove(itemToPull);
+
+            // Add update of list to pending tasks
+            context.addUpdateTask(entity, new MongoTask() {
+
+                @Override
+                public void execute() {
+                    // Pull item from DB
+                    Object dbItemToPull = mapperRegistry.convertApplicationObjectToDBObject(itemToPull, Object.class);
+                    BasicDBObject query = new BasicDBObject("_id", getObjectId(entity.getId()));
+                    BasicDBObject pullObject = new BasicDBObject(listPropertyName, dbItemToPull);
+                    BasicDBObject pullCommand = new BasicDBObject("$pull", pullObject);
+                    getDBCollectionForType(type).update(query, pullCommand);
+                }
+
+                @Override
+                public boolean isFullUpdate() {
+                    return false;
+                }
+            });
+
+            return true;
+        }
+    }
+
+    // Possibility to add user-defined converters
+    public void addAppObjectConverter(Mapper<?, ?> mapper) {
+        mapperRegistry.addAppObjectMapper(mapper);
+    }
+
+    public void addDBObjectConverter(Mapper<?, ?> mapper) {
+        mapperRegistry.addDBObjectMapper(mapper);
+    }
+
+    public EntityInfo getEntityInfo(Class<? extends MongoEntity> objectClass) {
+        EntityInfo entityInfo = entityInfoCache.get(objectClass);
+        if (entityInfo == null) {
+            List<Property<Object>> properties = PropertyQueries.createQuery(objectClass).addCriteria(new AnnotatedPropertyCriteria(MongoField.class)).getResultList();
+
+            MongoCollection classAnnotation = objectClass.getAnnotation(MongoCollection.class);
+
+            String dbCollectionName = classAnnotation==null ? null : classAnnotation.collectionName();
+            entityInfo = new EntityInfo(objectClass, dbCollectionName, properties);
+
+            EntityInfo existing = entityInfoCache.putIfAbsent(objectClass, entityInfo);
+            if (existing != null) {
+                entityInfo = existing;
+            }
+        }
+
+        return entityInfo;
+    }
+
+    protected <T extends MongoIdentifiableEntity> List<T> convertCursor(Class<T> type, DBCursor cursor, MongoStoreInvocationContext context) {
+        List<T> result = new ArrayList<T>();
+
+        try {
+            for (DBObject dbObject : cursor) {
+                // First look if we already have loaded object cached. If yes, we will use cached instance
+                String id = dbObject.get("_id").toString();
+                T object = context.getLoadedObject(type, id);
+
+                if (object == null) {
+                    // So convert and use fresh instance from DB
+                    MapperContext<Object, T> mapperContext = new MapperContext<Object, T>(dbObject, type, null);
+                    object = mapperRegistry.convertDBObjectToApplicationObject(mapperContext);
+                    context.addLoadedObject(object);
+                }
+
+                result.add(object);
+            }
+        } finally {
+            cursor.close();
+        }
+
+        return result;
+    }
+
+    protected DBCollection getDBCollectionForType(Class<? extends MongoEntity> type) {
+        EntityInfo entityInfo = getEntityInfo(type);
+        String dbCollectionName = entityInfo.getDbCollectionName();
+        return dbCollectionName==null ? null : database.getCollection(entityInfo.getDbCollectionName());
+    }
+
+    // We allow ObjectId to be both "ObjectId" or "String".
+    protected Object getObjectId(String idAsString) {
+        if (ObjectId.isValid(idAsString)) {
+            return new ObjectId(idAsString);
+        } else {
+            return idAsString;
+        }
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBListMapper.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBListMapper.java
new file mode 100755
index 0000000..ff81604
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBListMapper.java
@@ -0,0 +1,44 @@
+package org.keycloak.models.mongo.impl.types;
+
+import com.mongodb.BasicDBList;
+import org.keycloak.models.mongo.api.types.Mapper;
+import org.keycloak.models.mongo.api.types.MapperContext;
+import org.keycloak.models.mongo.api.types.MapperRegistry;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class BasicDBListMapper implements Mapper<BasicDBList, List> {
+
+    private final MapperRegistry mapperRegistry;
+
+    public BasicDBListMapper(MapperRegistry mapperRegistry) {
+        this.mapperRegistry = mapperRegistry;
+    }
+
+    @Override
+    public List convertObject(MapperContext<BasicDBList, List> context) {
+        BasicDBList dbList = context.getObjectToConvert();
+        ArrayList<Object> appObjects = new ArrayList<Object>();
+        Class<?> expectedListElementType = context.getGenericTypes().get(0);
+
+        for (Object dbObject : dbList) {
+            MapperContext<Object, Object> newContext = new MapperContext<Object, Object>(dbObject, expectedListElementType, null);
+            appObjects.add(mapperRegistry.convertDBObjectToApplicationObject(newContext));
+        }
+        return appObjects;
+    }
+
+    @Override
+    public Class<? extends BasicDBList> getTypeOfObjectToConvert() {
+        return BasicDBList.class;
+    }
+
+    @Override
+    public Class<List> getExpectedReturnType() {
+        return List.class;
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBObjectToMapMapper.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBObjectToMapMapper.java
new file mode 100644
index 0000000..47e8d3c
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/BasicDBObjectToMapMapper.java
@@ -0,0 +1,44 @@
+package org.keycloak.models.mongo.impl.types;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.mongodb.BasicDBObject;
+import org.keycloak.models.mongo.api.types.Mapper;
+import org.keycloak.models.mongo.api.types.MapperContext;
+
+/**
+ * For now, we support just convert to Map<String, String>
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class BasicDBObjectToMapMapper implements Mapper<BasicDBObject, Map> {
+
+    @Override
+    public Map convertObject(MapperContext<BasicDBObject, Map> context) {
+        BasicDBObject objectToConvert = context.getObjectToConvert();
+
+        HashMap<String, String> result = new HashMap<String, String>();
+        for (Map.Entry<String, Object> entry : objectToConvert.entrySet()) {
+            String key = entry.getKey();
+            String value = (String)entry.getValue();
+
+            if (key.contains(MapMapper.DOT_PLACEHOLDER)) {
+                key = key.replaceAll(MapMapper.DOT_PLACEHOLDER, ".");
+            }
+
+            result.put(key, value);
+        }
+        return result;
+    }
+
+    @Override
+    public Class<? extends BasicDBObject> getTypeOfObjectToConvert() {
+        return BasicDBObject.class;
+    }
+
+    @Override
+    public Class<Map> getExpectedReturnType() {
+        return Map.class;
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/MapMapper.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/MapMapper.java
new file mode 100644
index 0000000..4798ac1
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/MapMapper.java
@@ -0,0 +1,54 @@
+package org.keycloak.models.mongo.impl.types;
+
+import java.util.Map;
+import java.util.Set;
+
+import com.mongodb.BasicDBObject;
+import org.keycloak.models.mongo.api.types.Mapper;
+import org.keycloak.models.mongo.api.types.MapperContext;
+
+/**
+ * For now, we support just convert from Map<String, String>
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MapMapper<T extends Map> implements Mapper<T, BasicDBObject> {
+
+    // Just some dummy way of encoding . character as it's not allowed by mongo in key fields
+    static final String DOT_PLACEHOLDER = "###";
+
+    private final Class<T> mapType;
+
+    public MapMapper(Class<T> mapType) {
+        this.mapType = mapType;
+    }
+
+    @Override
+    public BasicDBObject convertObject(MapperContext<T, BasicDBObject> context) {
+        T objectToConvert = context.getObjectToConvert();
+
+        BasicDBObject dbObject = new BasicDBObject();
+        Set<Map.Entry> entries = objectToConvert.entrySet();
+        for (Map.Entry entry : entries) {
+            String key = (String)entry.getKey();
+            String value = (String)entry.getValue();
+
+            if (key.contains(".")) {
+                key = key.replaceAll("\\.", DOT_PLACEHOLDER);
+            }
+
+            dbObject.put(key, value);
+        }
+        return dbObject;
+    }
+
+    @Override
+    public Class<? extends T> getTypeOfObjectToConvert() {
+        return mapType;
+    }
+
+    @Override
+    public Class<BasicDBObject> getExpectedReturnType() {
+        return BasicDBObject.class;
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/MongoEntityMapper.java b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/MongoEntityMapper.java
new file mode 100755
index 0000000..3514e6e
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/impl/types/MongoEntityMapper.java
@@ -0,0 +1,58 @@
+package org.keycloak.models.mongo.impl.types;
+
+import com.mongodb.BasicDBObject;
+import org.keycloak.models.mongo.api.MongoEntity;
+import org.keycloak.models.mongo.api.types.Mapper;
+import org.keycloak.models.mongo.api.types.MapperContext;
+import org.keycloak.models.mongo.api.types.MapperRegistry;
+import org.keycloak.models.mongo.impl.MongoStoreImpl;
+import org.keycloak.models.mongo.impl.EntityInfo;
+import org.picketlink.common.properties.Property;
+
+import java.util.Collection;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MongoEntityMapper<T extends MongoEntity> implements Mapper<T, BasicDBObject> {
+
+    private final MongoStoreImpl mongoStoreImpl;
+    private final MapperRegistry mapperRegistry;
+    private final Class<T> expectedMongoEntityType;
+
+    public MongoEntityMapper(MongoStoreImpl mongoStoreImpl, MapperRegistry mapperRegistry, Class<T> expectedMongoEntityType) {
+        this.mongoStoreImpl = mongoStoreImpl;
+        this.mapperRegistry = mapperRegistry;
+        this.expectedMongoEntityType = expectedMongoEntityType;
+    }
+
+    @Override
+    public BasicDBObject convertObject(MapperContext<T, BasicDBObject> context) {
+        T applicationObject = context.getObjectToConvert();
+
+        EntityInfo entityInfo = mongoStoreImpl.getEntityInfo(applicationObject.getClass());
+
+        // Create instance of BasicDBObject and add all declared properties to it (properties with null value probably should be skipped)
+        BasicDBObject dbObject = new BasicDBObject();
+        Collection<Property<Object>> props = entityInfo.getProperties();
+        for (Property<Object> property : props) {
+            String propName = property.getName();
+            Object propValue = property.getValue(applicationObject);
+
+            Object dbValue = propValue == null ? null : mapperRegistry.convertApplicationObjectToDBObject(propValue, Object.class);
+            dbObject.put(propName, dbValue);
+        }
+
+        return dbObject;
+    }
+
+    @Override
+    public Class<? extends T> getTypeOfObjectToConvert() {
+        return expectedMongoEntityType;
+    }
+
+    @Override
+    public Class<BasicDBObject> getExpectedReturnType() {
+        return BasicDBObject.class;
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/AbstractAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/AbstractAdapter.java
new file mode 100644
index 0000000..9e4f945
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/AbstractAdapter.java
@@ -0,0 +1,40 @@
+package org.keycloak.models.mongo.keycloak.adapters;
+
+import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
+import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public abstract class AbstractAdapter {
+
+    protected MongoStoreInvocationContext invocationContext;
+
+    public AbstractAdapter(MongoStoreInvocationContext invocationContext) {
+        this.invocationContext = invocationContext;
+    }
+
+    public abstract AbstractMongoIdentifiableEntity getMongoEntity();
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) return true;
+
+        if (o == null || getClass() != o.getClass()) return false;
+
+        AbstractAdapter that = (AbstractAdapter) o;
+
+        if (getMongoEntity() == null && that.getMongoEntity() == null) return true;
+        return getMongoEntity().equals(that.getMongoEntity());
+    }
+
+    @Override
+    public int hashCode() {
+        return getMongoEntity()!=null ? getMongoEntity().hashCode() : super.hashCode();
+    }
+
+    protected MongoStore getMongoStore() {
+        return invocationContext.getMongoStore();
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java
index c1a4dd2..0686f42 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/ApplicationAdapter.java
@@ -1,13 +1,16 @@
 package org.keycloak.models.mongo.keycloak.adapters;
 
+import com.mongodb.DBObject;
+import com.mongodb.QueryBuilder;
 import org.keycloak.models.ApplicationModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserModel;
-import org.keycloak.models.mongo.api.NoSQL;
-import org.keycloak.models.mongo.api.query.NoSQLQuery;
-import org.keycloak.models.mongo.keycloak.data.ApplicationData;
-import org.keycloak.models.mongo.keycloak.data.RoleData;
-import org.keycloak.models.mongo.keycloak.data.UserData;
+import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.mongo.keycloak.entities.ApplicationEntity;
+import org.keycloak.models.mongo.keycloak.entities.RoleEntity;
+import org.keycloak.models.mongo.keycloak.entities.UserEntity;
+import org.keycloak.models.mongo.utils.MongoModelUtils;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -17,31 +20,38 @@ import java.util.Set;
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
-public class ApplicationAdapter implements ApplicationModel {
+public class ApplicationAdapter extends AbstractAdapter implements ApplicationModel {
 
-    private final ApplicationData application;
-    private final NoSQL noSQL;
+    private final ApplicationEntity application;
+    private UserAdapter resourceUser;
 
-    private UserData resourceUser;
+    public ApplicationAdapter(ApplicationEntity applicationEntity, MongoStoreInvocationContext invContext) {
+        this(applicationEntity, null, invContext);
+    }
 
-    public ApplicationAdapter(ApplicationData applicationData, NoSQL noSQL) {
-        this.application = applicationData;
-        this.noSQL = noSQL;
+    public ApplicationAdapter(ApplicationEntity applicationEntity, UserAdapter resourceUser, MongoStoreInvocationContext invContext) {
+        super(invContext);
+        this.application = applicationEntity;
+        this.resourceUser = resourceUser;
     }
 
     @Override
     public void updateApplication() {
-        noSQL.saveObject(application);
+        getMongoStore().updateEntity(application, invocationContext);
     }
 
     @Override
-    public UserModel getApplicationUser() {
+    public UserAdapter getApplicationUser() {
         // This is not thread-safe. Assumption is that ApplicationAdapter instance is per-client object
         if (resourceUser == null) {
-            resourceUser = noSQL.loadObject(UserData.class, application.getResourceUserId());
+            UserEntity userEntity = getMongoStore().loadEntity(UserEntity.class, application.getResourceUserId(), invocationContext);
+            if (userEntity == null) {
+                throw new IllegalStateException("User " + application.getResourceUserId() + " not found");
+            }
+            resourceUser = new UserAdapter(userEntity, invocationContext);
         }
 
-        return resourceUser != null ? new UserAdapter(resourceUser, noSQL) : null;
+        return resourceUser;
     }
 
     @Override
@@ -101,199 +111,127 @@ public class ApplicationAdapter implements ApplicationModel {
 
     @Override
     public RoleAdapter getRole(String name) {
-        NoSQLQuery query = noSQL.createQueryBuilder()
-                .andCondition("name", name)
-                .andCondition("applicationId", getId())
-                .build();
-        RoleData role = noSQL.loadSingleObject(RoleData.class, query);
+        DBObject query = new QueryBuilder()
+                .and("name").is(name)
+                .and("applicationId").is(getId())
+                .get();
+        RoleEntity role = getMongoStore().loadSingleEntity(RoleEntity.class, query, invocationContext);
         if (role == null) {
             return null;
         } else {
-            return new RoleAdapter(role, noSQL);
+            return new RoleAdapter(role, invocationContext);
         }
     }
 
     @Override
     public RoleModel getRoleById(String id) {
-        RoleData role = noSQL.loadObject(RoleData.class, id);
+        RoleEntity role = getMongoStore().loadEntity(RoleEntity.class, id, invocationContext);
         if (role == null) {
             return null;
         } else {
-            return new RoleAdapter(role, noSQL);
+            return new RoleAdapter(role, this, invocationContext);
         }
     }
 
     @Override
-    public void grantRole(UserModel user, RoleModel role) {
-        UserData userData = ((UserAdapter)user).getUser();
-        noSQL.pushItemToList(userData, "roleIds", role.getId());
-    }
+    public RoleAdapter addRole(String name) {
+        RoleAdapter existing = getRole(name);
+        if (existing != null) {
+            return existing;
+        }
 
-    @Override
-    public boolean hasRole(UserModel user, String role) {
-        RoleModel roleModel = getRole(role);
-        return hasRole(user, roleModel);
-    }
+        RoleEntity roleEntity = new RoleEntity();
+        roleEntity.setName(name);
+        roleEntity.setApplicationId(getId());
 
-    @Override
-    public boolean hasRole(UserModel user, RoleModel role) {
-        UserData userData = ((UserAdapter)user).getUser();
-
-        List<String> roleIds = userData.getRoleIds();
-        String roleId = role.getId();
-        if (roleIds != null) {
-            for (String currentId : roleIds) {
-                if (roleId.equals(currentId)) {
-                    return true;
-                }
-            }
-        }
-        return false;
+        getMongoStore().insertEntity(roleEntity, invocationContext);
+        return new RoleAdapter(roleEntity, this, invocationContext);
     }
 
     @Override
-    public RoleAdapter addRole(String name) {
-        if (getRole(name) != null) {
-            throw new IllegalArgumentException("Role " + name + " already exists");
-        }
-
-        RoleData roleData = new RoleData();
-        roleData.setName(name);
-        roleData.setApplicationId(getId());
-
-        noSQL.saveObject(roleData);
-        return new RoleAdapter(roleData, noSQL);
+    public boolean removeRoleById(String id) {
+        return getMongoStore().removeEntity(RoleEntity.class, id, invocationContext);
     }
 
     @Override
-    public List<RoleModel> getRoles() {
-        NoSQLQuery query = noSQL.createQueryBuilder()
-                .andCondition("applicationId", getId())
-                .build();
-        List<RoleData> roles = noSQL.loadObjects(RoleData.class, query);
-
-        List<RoleModel> result = new ArrayList<RoleModel>();
-        for (RoleData role : roles) {
-            result.add(new RoleAdapter(role, noSQL));
+    public Set<RoleModel> getRoles() {
+        DBObject query = new QueryBuilder()
+                .and("applicationId").is(getId())
+                .get();
+        List<RoleEntity> roles = getMongoStore().loadEntities(RoleEntity.class, query, invocationContext);
+
+        Set<RoleModel> result = new HashSet<RoleModel>();
+        for (RoleEntity role : roles) {
+            result.add(new RoleAdapter(role, this, invocationContext));
         }
 
         return result;
     }
 
-    // Static so that it can be used from RealmAdapter as well
-    static List<RoleData> getAllRolesOfUser(UserModel user, NoSQL noSQL) {
-        UserData userData = ((UserAdapter)user).getUser();
-        List<String> roleIds = userData.getRoleIds();
-
-        NoSQLQuery query = noSQL.createQueryBuilder()
-                .inCondition("_id", roleIds)
-                .build();
-        return noSQL.loadObjects(RoleData.class, query);
-    }
-
     @Override
-    public Set<String> getRoleMappingValues(UserModel user) {
-        Set<String> result = new HashSet<String>();
-        List<RoleData> roles = getAllRolesOfUser(user, noSQL);
-        // TODO: Maybe improve as currently we need to obtain all roles and then filter programmatically...
-        for (RoleData role : roles) {
+    public Set<RoleModel> getApplicationRoleMappings(UserModel user) {
+        Set<RoleModel> result = new HashSet<RoleModel>();
+        List<RoleEntity> roles = MongoModelUtils.getAllRolesOfUser(user, invocationContext);
+
+        for (RoleEntity role : roles) {
             if (getId().equals(role.getApplicationId())) {
-                result.add(role.getName());
+                result.add(new RoleAdapter(role, this, invocationContext));
             }
         }
         return result;
     }
 
     @Override
-    public List<RoleModel> getRoleMappings(UserModel user) {
-        List<RoleModel> result = new ArrayList<RoleModel>();
-        List<RoleData> roles = getAllRolesOfUser(user, noSQL);
-        // TODO: Maybe improve as currently we need to obtain all roles and then filter programmatically...
-        for (RoleData role : roles) {
+    public void addScope(RoleModel role) {
+        UserAdapter appUser = getApplicationUser();
+        getMongoStore().pushItemToList(appUser.getUser(), "scopeIds", role.getId(), true, invocationContext);
+    }
+
+    @Override
+    public Set<RoleModel> getApplicationScopeMappings(UserModel user) {
+        Set<RoleModel> result = new HashSet<RoleModel>();
+        List<RoleEntity> roles = MongoModelUtils.getAllScopesOfUser(user, invocationContext);
+
+        for (RoleEntity role : roles) {
             if (getId().equals(role.getApplicationId())) {
-                result.add(new RoleAdapter(role, noSQL));
+                result.add(new RoleAdapter(role, this, invocationContext));
             }
         }
         return result;
     }
 
     @Override
-    public void deleteRoleMapping(UserModel user, RoleModel role) {
-        UserData userData = ((UserAdapter)user).getUser();
-        noSQL.pullItemFromList(userData, "roleIds", role.getId());
+    public List<String> getDefaultRoles() {
+        return application.getDefaultRoles();
     }
 
     @Override
-    public void addScopeMapping(UserModel agent, String roleName) {
-        RoleAdapter role = getRole(roleName);
+    public void addDefaultRole(String name) {
+        RoleModel role = getRole(name);
         if (role == null) {
-            throw new RuntimeException("Role not found");
+            addRole(name);
         }
 
-        addScopeMapping(agent, role);
+        getMongoStore().pushItemToList(application, "defaultRoles", name, true, invocationContext);
     }
 
     @Override
-    public void addScopeMapping(UserModel agent, RoleModel role) {
-        UserData userData = ((UserAdapter)agent).getUser();
-        noSQL.pushItemToList(userData, "scopeIds", role.getId());
-    }
-
-    @Override
-    public void deleteScopeMapping(UserModel user, RoleModel role) {
-        UserData userData = ((UserAdapter)user).getUser();
-        noSQL.pullItemFromList(userData, "scopeIds", role.getId());
-    }
-
-    // Static so that it can be used from RealmAdapter as well
-    static List<RoleData> getAllScopesOfUser(UserModel user, NoSQL noSQL) {
-        UserData userData = ((UserAdapter)user).getUser();
-        List<String> roleIds = userData.getScopeIds();
-
-        NoSQLQuery query = noSQL.createQueryBuilder()
-                .inCondition("_id", roleIds)
-                .build();
-        return noSQL.loadObjects(RoleData.class, query);
-    }
-
-    @Override
-    public Set<String> getScopeMappingValues(UserModel agent) {
-        Set<String> result = new HashSet<String>();
-        List<RoleData> roles = getAllScopesOfUser(agent,  noSQL);
-        // TODO: Maybe improve as currently we need to obtain all roles and then filter programmatically...
-        for (RoleData role : roles) {
-            if (getId().equals(role.getApplicationId())) {
-                result.add(role.getName());
+    public void updateDefaultRoles(String[] defaultRoles) {
+        List<String> roleNames = new ArrayList<String>();
+        for (String roleName : defaultRoles) {
+            RoleModel role = getRole(roleName);
+            if (role == null) {
+                addRole(roleName);
             }
-        }
-        return result;
-    }
 
-    @Override
-    public List<RoleModel> getScopeMappings(UserModel agent) {
-        List<RoleModel> result = new ArrayList<RoleModel>();
-        List<RoleData> roles = getAllScopesOfUser(agent,  noSQL);
-        // TODO: Maybe improve as currently we need to obtain all roles and then filter programmatically...
-        for (RoleData role : roles) {
-            if (getId().equals(role.getApplicationId())) {
-                result.add(new RoleAdapter(role, noSQL));
-            }
+            roleNames.add(roleName);
         }
-        return result;
-    }
-
-    @Override
-    public List<String> getDefaultRoles() {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
-    }
 
-    @Override
-    public void addDefaultRole(String name) {
-        //To change body of implemented methods use File | Settings | File Templates.
+        application.setDefaultRoles(roleNames);
     }
 
     @Override
-    public void updateDefaultRoles(String[] defaultRoles) {
-        //To change body of implemented methods use File | Settings | File Templates.
+    public AbstractMongoIdentifiableEntity getMongoEntity() {
+        return application;
     }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakSessionFactory.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakSessionFactory.java
new file mode 100755
index 0000000..7ff4ead
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakSessionFactory.java
@@ -0,0 +1,70 @@
+package org.keycloak.models.mongo.keycloak.adapters;
+
+import com.mongodb.DB;
+import com.mongodb.MongoClient;
+import org.jboss.logging.Logger;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.mongo.api.MongoEntity;
+import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.impl.MongoStoreImpl;
+import org.keycloak.models.mongo.keycloak.entities.ApplicationEntity;
+import org.keycloak.models.mongo.keycloak.entities.CredentialEntity;
+import org.keycloak.models.mongo.keycloak.entities.OAuthClientEntity;
+import org.keycloak.models.mongo.keycloak.entities.RealmEntity;
+import org.keycloak.models.mongo.keycloak.entities.RequiredCredentialEntity;
+import org.keycloak.models.mongo.keycloak.entities.RoleEntity;
+import org.keycloak.models.mongo.keycloak.entities.SocialLinkEntity;
+import org.keycloak.models.mongo.keycloak.entities.UserEntity;
+import org.keycloak.models.mongo.utils.MongoConfiguration;
+
+import java.net.UnknownHostException;
+
+/**
+ * KeycloakSessionFactory implementation based on MongoDB
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MongoKeycloakSessionFactory implements KeycloakSessionFactory {
+    protected static final Logger logger = Logger.getLogger(MongoKeycloakSessionFactory.class);
+
+    private static final Class<? extends MongoEntity>[] MANAGED_ENTITY_TYPES = (Class<? extends MongoEntity>[])new Class<?>[] {
+            RealmEntity.class,
+            UserEntity.class,
+            RoleEntity.class,
+            RequiredCredentialEntity.class,
+            CredentialEntity.class,
+            SocialLinkEntity.class,
+            ApplicationEntity.class,
+            OAuthClientEntity.class
+    };
+
+    private final MongoClient mongoClient;
+    private final MongoStore mongoStore;
+
+    public MongoKeycloakSessionFactory(MongoConfiguration config) {
+        logger.info(String.format("Configuring MongoStore with: " + config));
+
+        try {
+            // TODO: authentication support
+            mongoClient = new MongoClient(config.getHost(), config.getPort());
+
+            DB db = mongoClient.getDB(config.getDbName());
+            mongoStore = new MongoStoreImpl(db, config.isClearCollectionsOnStartup(), MANAGED_ENTITY_TYPES);
+
+        } catch (UnknownHostException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public KeycloakSession createSession() {
+        return new MongoKeycloakSession(mongoStore);
+    }
+
+    @Override
+    public void close() {
+        logger.info("Closing MongoDB client");
+        mongoClient.close();
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java
index d522db9..2eacc45 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/OAuthClientAdapter.java
@@ -2,28 +2,27 @@ package org.keycloak.models.mongo.keycloak.adapters;
 
 import org.keycloak.models.OAuthClientModel;
 import org.keycloak.models.UserModel;
-import org.keycloak.models.mongo.api.NoSQL;
-import org.keycloak.models.mongo.keycloak.data.OAuthClientData;
-import org.keycloak.models.mongo.keycloak.data.UserData;
+import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.mongo.keycloak.entities.OAuthClientEntity;
+import org.keycloak.models.mongo.keycloak.entities.UserEntity;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
-public class OAuthClientAdapter implements OAuthClientModel {
+public class OAuthClientAdapter extends AbstractAdapter implements OAuthClientModel {
 
-    private final OAuthClientData delegate;
+    private final OAuthClientEntity delegate;
     private UserAdapter oauthAgent;
-    private final NoSQL noSQL;
 
-    public OAuthClientAdapter(OAuthClientData oauthClientData, UserAdapter oauthAgent, NoSQL noSQL) {
-        this.delegate = oauthClientData;
+    public OAuthClientAdapter(OAuthClientEntity oauthClientEntity, UserAdapter oauthAgent, MongoStoreInvocationContext invContext) {
+        super(invContext);
+        this.delegate = oauthClientEntity;
         this.oauthAgent = oauthAgent;
-        this.noSQL = noSQL;
     }
 
-    public OAuthClientAdapter(OAuthClientData oauthClientData, NoSQL noSQL) {
-        this.delegate = oauthClientData;
-        this.noSQL = noSQL;
+    public OAuthClientAdapter(OAuthClientEntity oauthClientEntity, MongoStoreInvocationContext invContext) {
+        this(oauthClientEntity, null, invContext);
     }
 
     @Override
@@ -35,10 +34,14 @@ public class OAuthClientAdapter implements OAuthClientModel {
     public UserModel getOAuthAgent() {
         // This is not thread-safe. Assumption is that OAuthClientAdapter instance is per-client object
         if (oauthAgent == null) {
-            UserData user = noSQL.loadObject(UserData.class, delegate.getOauthAgentId());
-            oauthAgent = user!=null ? new UserAdapter(user, noSQL) : null;
+            UserEntity user = getMongoStore().loadEntity(UserEntity.class, delegate.getOauthAgentId(), invocationContext);
+            oauthAgent = user!=null ? new UserAdapter(user, invocationContext) : null;
         }
         return oauthAgent;
     }
 
+    @Override
+    public AbstractMongoIdentifiableEntity getMongoEntity() {
+        return delegate;
+    }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
index 7d4aa72..6083eeb 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
@@ -1,63 +1,61 @@
 package org.keycloak.models.mongo.keycloak.adapters;
 
-import org.bouncycastle.openssl.PEMWriter;
-import org.keycloak.PemUtils;
+import com.mongodb.DBObject;
+import com.mongodb.QueryBuilder;
+import org.jboss.logging.Logger;
 import org.keycloak.models.ApplicationModel;
 import org.keycloak.models.OAuthClientModel;
+import org.keycloak.models.PasswordPolicy;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RequiredCredentialModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.SocialLinkModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserModel;
-import org.keycloak.models.mongo.api.NoSQL;
-import org.keycloak.models.mongo.api.query.NoSQLQuery;
-import org.keycloak.models.mongo.api.query.NoSQLQueryBuilder;
-import org.keycloak.models.mongo.keycloak.credentials.PasswordCredentialHandler;
-import org.keycloak.models.mongo.keycloak.credentials.TOTPCredentialHandler;
-import org.keycloak.models.mongo.keycloak.data.ApplicationData;
-import org.keycloak.models.mongo.keycloak.data.OAuthClientData;
-import org.keycloak.models.mongo.keycloak.data.RealmData;
-import org.keycloak.models.mongo.keycloak.data.RequiredCredentialData;
-import org.keycloak.models.mongo.keycloak.data.RoleData;
-import org.keycloak.models.mongo.keycloak.data.SocialLinkData;
-import org.keycloak.models.mongo.keycloak.data.UserData;
-import org.keycloak.representations.idm.CredentialRepresentation;
-import org.picketlink.idm.credential.Credentials;
-
-import java.io.IOException;
-import java.io.StringWriter;
+import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.mongo.keycloak.entities.ApplicationEntity;
+import org.keycloak.models.mongo.keycloak.entities.CredentialEntity;
+import org.keycloak.models.mongo.keycloak.entities.OAuthClientEntity;
+import org.keycloak.models.mongo.keycloak.entities.RealmEntity;
+import org.keycloak.models.mongo.keycloak.entities.RequiredCredentialEntity;
+import org.keycloak.models.mongo.keycloak.entities.RoleEntity;
+import org.keycloak.models.mongo.keycloak.entities.SocialLinkEntity;
+import org.keycloak.models.mongo.keycloak.entities.UserEntity;
+import org.keycloak.models.mongo.utils.MongoModelUtils;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
+import org.keycloak.models.utils.TimeBasedOTP;
+
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.regex.Pattern;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
-public class RealmAdapter implements RealmModel {
+public class RealmAdapter extends AbstractAdapter implements RealmModel {
+
+    private static final Logger logger = Logger.getLogger(RealmAdapter.class);
 
-    private final RealmData realm;
-    private final NoSQL noSQL;
+    private final RealmEntity realm;
 
     protected volatile transient PublicKey publicKey;
     protected volatile transient PrivateKey privateKey;
 
-    // TODO: likely shouldn't be static. And ATM, just empty map is passed -> It's not possible to configure stuff like PasswordEncoder etc.
-    private static PasswordCredentialHandler passwordCredentialHandler = new PasswordCredentialHandler(new HashMap<String, Object>());
-    private static TOTPCredentialHandler totpCredentialHandler = new TOTPCredentialHandler(new HashMap<String, Object>());
-
-    public RealmAdapter(RealmData realmData, NoSQL noSQL) {
-        this.realm = realmData;
-        this.noSQL = noSQL;
-    }
+    private volatile transient PasswordPolicy passwordPolicy;
 
-    protected String getOid() {
-        return realm.getOid();
+    public RealmAdapter(RealmEntity realmEntity, MongoStoreInvocationContext invocationContext) {
+        super(invocationContext);
+        this.realm = realmEntity;
     }
 
     @Override
@@ -88,79 +86,83 @@ public class RealmAdapter implements RealmModel {
     }
 
     @Override
-    public boolean isSocial() {
-        return realm.isSocial();
+    public boolean isSslNotRequired() {
+        return realm.isSslNotRequired();
     }
 
     @Override
-    public void setSocial(boolean social) {
-        realm.setSocial(social);
+    public void setSslNotRequired(boolean sslNotRequired) {
+        realm.setSslNotRequired(sslNotRequired);
         updateRealm();
     }
 
     @Override
-    public boolean isAutomaticRegistrationAfterSocialLogin() {
-        return realm.isAutomaticRegistrationAfterSocialLogin();
+    public boolean isRegistrationAllowed() {
+        return realm.isRegistrationAllowed();
     }
 
     @Override
-    public void setAutomaticRegistrationAfterSocialLogin(boolean automaticRegistrationAfterSocialLogin) {
-        realm.setAutomaticRegistrationAfterSocialLogin(automaticRegistrationAfterSocialLogin);
+    public void setRegistrationAllowed(boolean registrationAllowed) {
+        realm.setRegistrationAllowed(registrationAllowed);
         updateRealm();
     }
 
     @Override
-    public boolean isSslNotRequired() {
-        return realm.isSslNotRequired();
+    public boolean isVerifyEmail() {
+        return realm.isVerifyEmail();
     }
 
     @Override
-    public void setSslNotRequired(boolean sslNotRequired) {
-        realm.setSslNotRequired(sslNotRequired);
+    public void setVerifyEmail(boolean verifyEmail) {
+        realm.setVerifyEmail(verifyEmail);
         updateRealm();
     }
 
     @Override
-    public boolean isCookieLoginAllowed() {
-        return realm.isCookieLoginAllowed();
+    public boolean isResetPasswordAllowed() {
+        return realm.isResetPasswordAllowed();
     }
 
     @Override
-    public void setCookieLoginAllowed(boolean cookieLoginAllowed) {
-        realm.setCookieLoginAllowed(cookieLoginAllowed);
+    public void setResetPasswordAllowed(boolean resetPassword) {
+        realm.setResetPasswordAllowed(resetPassword);
         updateRealm();
     }
 
     @Override
-    public boolean isRegistrationAllowed() {
-        return realm.isRegistrationAllowed();
+    public boolean isSocial() {
+        return realm.isSocial();
     }
 
     @Override
-    public void setRegistrationAllowed(boolean registrationAllowed) {
-        realm.setRegistrationAllowed(registrationAllowed);
+    public void setSocial(boolean social) {
+        realm.setSocial(social);
         updateRealm();
     }
 
     @Override
-    public boolean isVerifyEmail() {
-        return realm.isVerifyEmail();
+    public boolean isUpdateProfileOnInitialSocialLogin() {
+        return realm.isUpdateProfileOnInitialSocialLogin();
     }
 
     @Override
-    public void setVerifyEmail(boolean verifyEmail) {
-        realm.setVerifyEmail(verifyEmail);
+    public void setUpdateProfileOnInitialSocialLogin(boolean updateProfileOnInitialSocialLogin) {
+        realm.setUpdateProfileOnInitialSocialLogin(updateProfileOnInitialSocialLogin);
         updateRealm();
     }
 
     @Override
-    public boolean isResetPasswordAllowed() {
-        return realm.isResetPasswordAllowed();
+    public PasswordPolicy getPasswordPolicy() {
+        if (passwordPolicy == null) {
+            passwordPolicy = new PasswordPolicy(realm.getPasswordPolicy());
+        }
+        return passwordPolicy;
     }
 
     @Override
-    public void setResetPasswordAllowed(boolean resetPassword) {
-        realm.setResetPasswordAllowed(resetPassword);
+    public void setPasswordPolicy(PasswordPolicy policy) {
+        this.passwordPolicy = policy;
+        realm.setPasswordPolicy(policy.toString());
         updateRealm();
     }
 
@@ -224,197 +226,238 @@ public class RealmAdapter implements RealmModel {
     @Override
     public PublicKey getPublicKey() {
         if (publicKey != null) return publicKey;
-        String pem = getPublicKeyPem();
-        if (pem != null) {
-            try {
-                publicKey = PemUtils.decodePublicKey(pem);
-            } catch (Exception e) {
-                throw new RuntimeException(e);
-            }
-        }
+        publicKey = KeycloakModelUtils.getPublicKey(getPublicKeyPem());
         return publicKey;
     }
 
     @Override
     public void setPublicKey(PublicKey publicKey) {
         this.publicKey = publicKey;
-        StringWriter writer = new StringWriter();
-        PEMWriter pemWriter = new PEMWriter(writer);
-        try {
-            pemWriter.writeObject(publicKey);
-            pemWriter.flush();
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-        String s = writer.toString();
-        setPublicKeyPem(PemUtils.removeBeginEnd(s));
+        String publicKeyPem = KeycloakModelUtils.getPemFromKey(publicKey);
+        setPublicKeyPem(publicKeyPem);
     }
 
     @Override
     public PrivateKey getPrivateKey() {
         if (privateKey != null) return privateKey;
-        String pem = getPrivateKeyPem();
-        if (pem != null) {
-            try {
-                privateKey = PemUtils.decodePrivateKey(pem);
-            } catch (Exception e) {
-                throw new RuntimeException(e);
-            }
-        }
+        privateKey = KeycloakModelUtils.getPrivateKey(getPrivateKeyPem());
         return privateKey;
     }
 
     @Override
     public void setPrivateKey(PrivateKey privateKey) {
         this.privateKey = privateKey;
-        StringWriter writer = new StringWriter();
-        PEMWriter pemWriter = new PEMWriter(writer);
-        try {
-            pemWriter.writeObject(privateKey);
-            pemWriter.flush();
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-        String s = writer.toString();
-        setPrivateKeyPem(PemUtils.removeBeginEnd(s));
+        String privateKeyPem = KeycloakModelUtils.getPemFromKey(privateKey);
+        setPrivateKeyPem(privateKeyPem);
+    }
+
+    @Override
+    public String getLoginTheme() {
+        return realm.getLoginTheme();
+    }
+
+    @Override
+    public void setLoginTheme(String name) {
+        realm.setLoginTheme(name);
+        updateRealm();
+    }
+
+    @Override
+    public String getAccountTheme() {
+        return realm.getAccountTheme();
+    }
+
+    @Override
+    public void setAccountTheme(String name) {
+        realm.setAccountTheme(name);
+        updateRealm();
     }
 
     @Override
     public UserAdapter getUser(String name) {
-        NoSQLQuery query = noSQL.createQueryBuilder()
-                .andCondition("loginName", name)
-                .andCondition("realmId", getOid())
-                .build();
-        UserData user = noSQL.loadSingleObject(UserData.class, query);
+        DBObject query = new QueryBuilder()
+                .and("loginName").is(name)
+                .and("realmId").is(getId())
+                .get();
+        UserEntity user = getMongoStore().loadSingleEntity(UserEntity.class, query, invocationContext);
+
+        if (user == null) {
+            return null;
+        } else {
+            return new UserAdapter(user, invocationContext);
+        }
+    }
+
+    @Override
+    public UserModel getUserByEmail(String email) {
+        DBObject query = new QueryBuilder()
+                .and("email").is(email)
+                .and("realmId").is(getId())
+                .get();
+        UserEntity user = getMongoStore().loadSingleEntity(UserEntity.class, query, invocationContext);
 
         if (user == null) {
             return null;
         } else {
-            return new UserAdapter(user, noSQL);
+            return new UserAdapter(user, invocationContext);
         }
     }
 
     @Override
     public UserAdapter addUser(String username) {
+        UserAdapter userModel = addUserEntity(username);
+
+        for (String r : getDefaultRoles()) {
+            grantRole(userModel, getRole(r));
+        }
+
+        for (ApplicationModel application : getApplications()) {
+            for (String r : application.getDefaultRoles()) {
+                grantRole(userModel, application.getRole(r));
+            }
+        }
+
+        return userModel;
+    }
+
+    // Add just user entity without defaultRoles
+    protected UserAdapter addUserEntity(String username) {
         if (getUser(username) != null) {
             throw new IllegalArgumentException("User " + username + " already exists");
         }
 
-        UserData userData = new UserData();
-        userData.setLoginName(username);
-        userData.setEnabled(true);
-        userData.setRealmId(getOid());
+        UserEntity userEntity = new UserEntity();
+        userEntity.setLoginName(username);
+        userEntity.setEnabled(true);
+        userEntity.setRealmId(getId());
 
-        noSQL.saveObject(userData);
-        return new UserAdapter(userData, noSQL);
+        getMongoStore().insertEntity(userEntity, invocationContext);
+        return new UserAdapter(userEntity, invocationContext);
     }
 
-    // This method doesn't exists on interface actually
-    public void removeUser(String name) {
-        NoSQLQuery query = noSQL.createQueryBuilder()
-                .andCondition("loginName", name)
-                .andCondition("realmId", getOid())
-                .build();
-        noSQL.removeObjects(UserData.class, query);
+    @Override
+    public boolean removeUser(String name) {
+        DBObject query = new QueryBuilder()
+                .and("loginName").is(name)
+                .and("realmId").is(getId())
+                .get();
+        return getMongoStore().removeEntities(UserEntity.class, query, invocationContext);
     }
 
     @Override
     public RoleAdapter getRole(String name) {
-        NoSQLQuery query = noSQL.createQueryBuilder()
-                .andCondition("name", name)
-                .andCondition("realmId", getOid())
-                .build();
-        RoleData role = noSQL.loadSingleObject(RoleData.class, query);
+        DBObject query = new QueryBuilder()
+                .and("name").is(name)
+                .and("realmId").is(getId())
+                .get();
+        RoleEntity role = getMongoStore().loadSingleEntity(RoleEntity.class, query, invocationContext);
         if (role == null) {
             return null;
         } else {
-            return new RoleAdapter(role, noSQL);
+            return new RoleAdapter(role, this, invocationContext);
         }
     }
 
     @Override
     public RoleModel addRole(String name) {
-        if (getRole(name) != null) {
-            throw new IllegalArgumentException("Role " + name + " already exists");
+        RoleAdapter role = getRole(name);
+        if (role != null) {
+            // Compatibility with JPA model
+            return role;
+            // throw new IllegalArgumentException("Role " + name + " already exists");
         }
 
-        RoleData roleData = new RoleData();
-        roleData.setName(name);
-        roleData.setRealmId(getOid());
+        RoleEntity roleEntity = new RoleEntity();
+        roleEntity.setName(name);
+        roleEntity.setRealmId(getId());
+
+        getMongoStore().insertEntity(roleEntity, invocationContext);
+        return new RoleAdapter(roleEntity, this, invocationContext);
+    }
 
-        noSQL.saveObject(roleData);
-        return new RoleAdapter(roleData, noSQL);
+    @Override
+    public boolean removeRoleById(String id) {
+        return getMongoStore().removeEntity(RoleEntity.class, id, invocationContext);
     }
 
     @Override
-    public List<RoleModel> getRoles() {
-        NoSQLQuery query = noSQL.createQueryBuilder()
-                .andCondition("realmId", getOid())
-                .build();
-        List<RoleData> roles = noSQL.loadObjects(RoleData.class, query);
+    public Set<RoleModel> getRoles() {
+        DBObject query = new QueryBuilder()
+                .and("realmId").is(getId())
+                .get();
+        List<RoleEntity> roles = getMongoStore().loadEntities(RoleEntity.class, query, invocationContext);
+
+        Set<RoleModel> result = new HashSet<RoleModel>();
 
-        List<RoleModel> result = new ArrayList<RoleModel>();
-        for (RoleData role : roles) {
-            result.add(new RoleAdapter(role, noSQL));
+        if (roles == null) return result;
+        for (RoleEntity role : roles) {
+            result.add(new RoleAdapter(role, this, invocationContext));
         }
 
         return result;
     }
 
     @Override
-    public List<RoleModel> getDefaultRoles() {
-        List<String> defaultRoles = realm.getDefaultRoles();
-
-        NoSQLQuery query = noSQL.createQueryBuilder()
-                .inCondition("_id", defaultRoles)
-                .build();
-        List<RoleData> defaultRolesData = noSQL.loadObjects(RoleData.class, query);
-
-        List<RoleModel> defaultRoleModels = new ArrayList<RoleModel>();
-        for (RoleData roleData : defaultRolesData) {
-            defaultRoleModels.add(new RoleAdapter(roleData, noSQL));
+    public RoleModel getRoleById(String id) {
+        RoleEntity role = getMongoStore().loadEntity(RoleEntity.class, id, invocationContext);
+        if (role == null) {
+            return null;
+        } else {
+            return new RoleAdapter(role, this, invocationContext);
         }
-        return defaultRoleModels;
+    }
+
+    @Override
+    public List<String> getDefaultRoles() {
+        return realm.getDefaultRoles();
     }
 
     @Override
     public void addDefaultRole(String name) {
         RoleModel role = getRole(name);
         if (role == null) {
-            role = addRole(name);
+            addRole(name);
         }
 
-        noSQL.pushItemToList(realm, "defaultRoles", role.getId());
+        getMongoStore().pushItemToList(realm, "defaultRoles", name, true, invocationContext);
     }
 
     @Override
     public void updateDefaultRoles(String[] defaultRoles) {
-        // defaultRoles is array with names of roles. So we need to convert to array of ids
-        List<String> roleIds = new ArrayList<String>();
+        List<String> roleNames = new ArrayList<String>();
         for (String roleName : defaultRoles) {
             RoleModel role = getRole(roleName);
             if (role == null) {
-                role = addRole(roleName);
+                addRole(roleName);
             }
 
-            roleIds.add(role.getId());
+            roleNames.add(roleName);
         }
 
-        realm.setDefaultRoles(roleIds);
+        realm.setDefaultRoles(roleNames);
         updateRealm();
     }
 
     @Override
     public ApplicationModel getApplicationById(String id) {
-        ApplicationData appData = noSQL.loadObject(ApplicationData.class, id);
+        ApplicationEntity appData = getMongoStore().loadEntity(ApplicationEntity.class, id, invocationContext);
 
         // Check if application belongs to this realm
-        if (appData == null || !getOid().equals(appData.getRealmId())) {
+        if (appData == null || !getId().equals(appData.getRealmId())) {
             return null;
         }
 
-        ApplicationModel model = new ApplicationAdapter(appData, noSQL);
-        return model;
+        return new ApplicationAdapter(appData, invocationContext);
+    }
+
+    @Override
+    public ApplicationModel getApplicationByName(String name) {
+        DBObject query = new QueryBuilder()
+                .and("realmId").is(getId())
+                .and("name").is(name)
+                .get();
+        ApplicationEntity appEntity = getMongoStore().loadSingleEntity(ApplicationEntity.class, query, invocationContext);
+        return appEntity==null ? null : new ApplicationAdapter(appEntity, invocationContext);
     }
 
     @Override
@@ -428,356 +471,355 @@ public class RealmAdapter implements RealmModel {
 
     @Override
     public List<ApplicationModel> getApplications() {
-        NoSQLQuery query = noSQL.createQueryBuilder()
-                .andCondition("realmId", getOid())
-                .build();
-        List<ApplicationData> appDatas = noSQL.loadObjects(ApplicationData.class, query);
+        DBObject query = new QueryBuilder()
+                .and("realmId").is(getId())
+                .get();
+        List<ApplicationEntity> appDatas = getMongoStore().loadEntities(ApplicationEntity.class, query, invocationContext);
 
         List<ApplicationModel> result = new ArrayList<ApplicationModel>();
-        for (ApplicationData appData : appDatas) {
-            result.add(new ApplicationAdapter(appData, noSQL));
+        for (ApplicationEntity appData : appDatas) {
+            result.add(new ApplicationAdapter(appData, invocationContext));
         }
         return result;
     }
 
     @Override
     public ApplicationModel addApplication(String name) {
-        UserAdapter resourceUser = addUser(name);
+        UserAdapter resourceUser = addUserEntity(name);
 
-        ApplicationData appData = new ApplicationData();
+        ApplicationEntity appData = new ApplicationEntity();
         appData.setName(name);
-        appData.setRealmId(getOid());
+        appData.setRealmId(getId());
+        appData.setEnabled(true);
         appData.setResourceUserId(resourceUser.getUser().getId());
-        noSQL.saveObject(appData);
+        getMongoStore().insertEntity(appData, invocationContext);
 
-        ApplicationModel resource = new ApplicationAdapter(appData, noSQL);
-        return resource;
+        return new ApplicationAdapter(appData, resourceUser, invocationContext);
+    }
+
+    @Override
+    public boolean removeApplication(String id) {
+        return getMongoStore().removeEntity(ApplicationEntity.class, id, invocationContext);
     }
 
     @Override
     public boolean hasRole(UserModel user, RoleModel role) {
-        UserData userData = ((UserAdapter)user).getUser();
-
-        List<String> roleIds = userData.getRoleIds();
-        String roleId = role.getId();
-        if (roleIds != null) {
-            for (String currentId : roleIds) {
-                if (roleId.equals(currentId)) {
-                    return true;
-                }
-            }
+        Set<RoleModel> roles = getRoleMappings(user);
+        if (roles.contains(role)) return true;
+
+        for (RoleModel mapping : roles) {
+            if (mapping.hasRole(role)) return true;
         }
         return false;
     }
 
     @Override
     public void grantRole(UserModel user, RoleModel role) {
-        UserData userData = ((UserAdapter)user).getUser();
-        noSQL.pushItemToList(userData, "roleIds", role.getId());
+        UserEntity userEntity = ((UserAdapter)user).getUser();
+        getMongoStore().pushItemToList(userEntity, "roleIds", role.getId(), true, invocationContext);
     }
 
     @Override
-    public List<RoleModel> getRoleMappings(UserModel user) {
-        List<RoleModel> result = new ArrayList<RoleModel>();
-        List<RoleData> roles = ApplicationAdapter.getAllRolesOfUser(user, noSQL);
-        // TODO: Maybe improve as currently we need to obtain all roles and then filter programmatically...
-        for (RoleData role : roles) {
-            if (getOid().equals(role.getRealmId())) {
-                result.add(new RoleAdapter(role, noSQL));
+    public Set<RoleModel> getRoleMappings(UserModel user) {
+        Set<RoleModel> result = new HashSet<RoleModel>();
+        List<RoleEntity> roles = MongoModelUtils.getAllRolesOfUser(user, invocationContext);
+
+        for (RoleEntity role : roles) {
+            if (getId().equals(role.getRealmId())) {
+                result.add(new RoleAdapter(role, this, invocationContext));
+            } else {
+                // Likely applicationRole, but we don't have this application yet
+                result.add(new RoleAdapter(role, invocationContext));
             }
         }
         return result;
     }
 
     @Override
-    public Set<String> getRoleMappingValues(UserModel user) {
-        Set<String> result = new HashSet<String>();
-        List<RoleData> roles = ApplicationAdapter.getAllRolesOfUser(user, noSQL);
-        // TODO: Maybe improve as currently we need to obtain all roles and then filter programmatically...
-        for (RoleData role : roles) {
-            if (getOid().equals(role.getRealmId())) {
-                result.add(role.getName());
+    public Set<RoleModel> getRealmRoleMappings(UserModel user) {
+        Set<RoleModel> allRoles = getRoleMappings(user);
+
+        // Filter to retrieve just realm roles TODO: Maybe improve to avoid filter programmatically... Maybe have separate fields for realmRoles and appRoles on user?
+        Set<RoleModel> realmRoles = new HashSet<RoleModel>();
+        for (RoleModel role : allRoles) {
+            RoleEntity roleEntity = ((RoleAdapter)role).getRole();
+
+            if (getId().equals(roleEntity.getRealmId())) {
+                realmRoles.add(role);
             }
         }
-        return result;
+        return realmRoles;
     }
 
     @Override
     public void deleteRoleMapping(UserModel user, RoleModel role) {
-        UserData userData = ((UserAdapter)user).getUser();
-        noSQL.pullItemFromList(userData, "roleIds", role.getId());
+        UserEntity userEntity = ((UserAdapter)user).getUser();
+        getMongoStore().pullItemFromList(userEntity, "roleIds", role.getId(), invocationContext);
     }
 
     @Override
-    public void addScopeMapping(UserModel agent, String roleName) {
-        RoleAdapter role = getRole(roleName);
-        if (role == null) {
-            throw new RuntimeException("Role not found");
+    public Set<RoleModel> getScopeMappings(UserModel user) {
+        Set<RoleModel> result = new HashSet<RoleModel>();
+        List<RoleEntity> roles = MongoModelUtils.getAllScopesOfUser(user, invocationContext);
+
+        for (RoleEntity role : roles) {
+            if (getId().equals(role.getRealmId())) {
+                result.add(new RoleAdapter(role, this, invocationContext));
+            } else {
+                // Likely applicationRole, but we don't have this application yet
+                result.add(new RoleAdapter(role, invocationContext));
+            }
         }
+        return result;
+    }
+
+    @Override
+    public Set<RoleModel> getRealmScopeMappings(UserModel user) {
+        Set<RoleModel> allScopes = getScopeMappings(user);
 
-        addScopeMapping(agent, role);
+        // Filter to retrieve just realm roles TODO: Maybe improve to avoid filter programmatically... Maybe have separate fields for realmRoles and appRoles on user?
+        Set<RoleModel> realmRoles = new HashSet<RoleModel>();
+        for (RoleModel role : allScopes) {
+            RoleEntity roleEntity = ((RoleAdapter)role).getRole();
+
+            if (getId().equals(roleEntity.getRealmId())) {
+                realmRoles.add(role);
+            }
+        }
+        return realmRoles;
     }
 
     @Override
     public void addScopeMapping(UserModel agent, RoleModel role) {
-        UserData userData = ((UserAdapter)agent).getUser();
-        noSQL.pushItemToList(userData, "scopeIds", role.getId());
+        UserEntity userEntity = ((UserAdapter)agent).getUser();
+        getMongoStore().pushItemToList(userEntity, "scopeIds", role.getId(), true, invocationContext);
     }
 
     @Override
     public void deleteScopeMapping(UserModel user, RoleModel role) {
-        UserData userData = ((UserAdapter)user).getUser();
-        noSQL.pullItemFromList(userData, "scopeIds", role.getId());
+        UserEntity userEntity = ((UserAdapter)user).getUser();
+        getMongoStore().pullItemFromList(userEntity, "scopeIds", role.getId(), invocationContext);
     }
 
     @Override
     public OAuthClientModel addOAuthClient(String name) {
-        UserAdapter oauthAgent = addUser(name);
+        UserAdapter oauthAgent = addUserEntity(name);
 
-        OAuthClientData oauthClient = new OAuthClientData();
+        OAuthClientEntity oauthClient = new OAuthClientEntity();
         oauthClient.setOauthAgentId(oauthAgent.getUser().getId());
-        oauthClient.setRealmId(getOid());
-        noSQL.saveObject(oauthClient);
+        oauthClient.setRealmId(getId());
+        oauthClient.setName(name);
+        getMongoStore().insertEntity(oauthClient, invocationContext);
 
-        return new OAuthClientAdapter(oauthClient, oauthAgent, noSQL);
+        return new OAuthClientAdapter(oauthClient, oauthAgent, invocationContext);
     }
 
     @Override
-    public OAuthClientModel getOAuthClient(String name) {
-        UserAdapter user = getUser(name);
-        if (user == null) return null;
-        NoSQLQuery query = noSQL.createQueryBuilder()
-                .andCondition("realmId", getOid())
-                .andCondition("oauthAgentId", user.getUser().getId())
-                .build();
-        OAuthClientData oauthClient = noSQL.loadSingleObject(OAuthClientData.class, query);
-        return oauthClient == null ? null : new OAuthClientAdapter(oauthClient, user, noSQL);
-    }
-
-    @Override
-    public List<OAuthClientModel> getOAuthClients() {
-        NoSQLQuery query = noSQL.createQueryBuilder()
-                .andCondition("realmId", getOid())
-                .build();
-        List<OAuthClientData> results = noSQL.loadObjects(OAuthClientData.class, query);
-        List<OAuthClientModel> list = new ArrayList<OAuthClientModel>();
-        for (OAuthClientData data : results) {
-            list.add(new OAuthClientAdapter(data, noSQL));
-        }
-        return list;
+    public boolean removeOAuthClient(String id) {
+        return getMongoStore().removeEntity(OAuthClientEntity.class, id, invocationContext);
     }
 
     @Override
-    public List<RoleModel> getScopeMappings(UserModel agent) {
-        List<RoleModel> result = new ArrayList<RoleModel>();
-        List<RoleData> roles = ApplicationAdapter.getAllScopesOfUser(agent, noSQL);
-        // TODO: Maybe improve as currently we need to obtain all roles and then filter programmatically...
-        for (RoleData role : roles) {
-            if (getOid().equals(role.getRealmId())) {
-                result.add(new RoleAdapter(role, noSQL));
-            }
-        }
-        return result;
+    public OAuthClientModel getOAuthClient(String name) {
+        UserAdapter user = getUser(name);
+        if (user == null) return null;
+        DBObject query = new QueryBuilder()
+                .and("realmId").is(getId())
+                .and("oauthAgentId").is(user.getUser().getId())
+                .get();
+        OAuthClientEntity oauthClient = getMongoStore().loadSingleEntity(OAuthClientEntity.class, query, invocationContext);
+        return oauthClient == null ? null : new OAuthClientAdapter(oauthClient, user, invocationContext);
     }
 
     @Override
-    public Set<String> getScopeMappingValues(UserModel agent) {
-        Set<String> result = new HashSet<String>();
-        List<RoleData> roles = ApplicationAdapter.getAllScopesOfUser(agent, noSQL);
-        // TODO: Maybe improve as currently we need to obtain all roles and then filter programmatically...
-        for (RoleData role : roles) {
-            if (getOid().equals(role.getRealmId())) {
-                result.add(role.getName());
-            }
-        }
-        return result;
+    public OAuthClientModel getOAuthClientById(String id) {
+        OAuthClientEntity clientEntity = getMongoStore().loadEntity(OAuthClientEntity.class, id, invocationContext);
+        if (clientEntity == null) return null;
+        return new OAuthClientAdapter(clientEntity, invocationContext);
     }
 
     @Override
-    public RoleModel getRoleById(String id) {
-        RoleData role = noSQL.loadObject(RoleData.class, id);
-        if (role == null) {
-            return null;
-        } else {
-            return new RoleAdapter(role, noSQL);
+    public List<OAuthClientModel> getOAuthClients() {
+        DBObject query = new QueryBuilder()
+                .and("realmId").is(getId())
+                .get();
+        List<OAuthClientEntity> results = getMongoStore().loadEntities(OAuthClientEntity.class, query, invocationContext);
+        List<OAuthClientModel> list = new ArrayList<OAuthClientModel>();
+        for (OAuthClientEntity data : results) {
+            list.add(new OAuthClientAdapter(data, invocationContext));
         }
+        return list;
     }
 
     @Override
-    public boolean hasRole(UserModel user, String role) {
-        RoleModel roleModel = getRole(role);
-        return hasRole(user, roleModel);
-    }
-
-    @Override
-    public void addRequiredCredential(String cred) {
-        RequiredCredentialModel credentialModel = initRequiredCredentialModel(cred);
-        addRequiredCredential(credentialModel, RequiredCredentialData.CLIENT_TYPE_USER);
+    public void addRequiredCredential(String type) {
+        RequiredCredentialModel credentialModel = initRequiredCredentialModel(type);
+        addRequiredCredential(credentialModel, realm.getRequiredCredentials());
     }
 
     @Override
     public void addRequiredResourceCredential(String type) {
         RequiredCredentialModel credentialModel = initRequiredCredentialModel(type);
-        addRequiredCredential(credentialModel, RequiredCredentialData.CLIENT_TYPE_RESOURCE);
+        addRequiredCredential(credentialModel, realm.getRequiredApplicationCredentials());
     }
 
     @Override
     public void addRequiredOAuthClientCredential(String type) {
         RequiredCredentialModel credentialModel = initRequiredCredentialModel(type);
-        addRequiredCredential(credentialModel, RequiredCredentialData.CLIENT_TYPE_OAUTH_RESOURCE);
+        addRequiredCredential(credentialModel, realm.getRequiredOAuthClientCredentials());
     }
 
-    protected void addRequiredCredential(RequiredCredentialModel credentialModel, int clientType) {
-        RequiredCredentialData credData = new RequiredCredentialData();
-        credData.setType(credentialModel.getType());
-        credData.setFormLabel(credentialModel.getFormLabel());
-        credData.setInput(credentialModel.isInput());
-        credData.setSecret(credentialModel.isSecret());
+    protected void addRequiredCredential(RequiredCredentialModel credentialModel, List<RequiredCredentialEntity> persistentCollection) {
+        RequiredCredentialEntity credEntity = new RequiredCredentialEntity();
+        credEntity.setType(credentialModel.getType());
+        credEntity.setFormLabel(credentialModel.getFormLabel());
+        credEntity.setInput(credentialModel.isInput());
+        credEntity.setSecret(credentialModel.isSecret());
 
-        credData.setRealmId(getOid());
-        credData.setClientType(clientType);
+        persistentCollection.add(credEntity);
 
-        noSQL.saveObject(credData);
+        updateRealm();
     }
 
     @Override
     public void updateRequiredCredentials(Set<String> creds) {
-        List<RequiredCredentialData> credsData = getRequiredCredentialsData(RequiredCredentialData.CLIENT_TYPE_USER);
-        updateRequiredCredentials(creds, credsData);
+        updateRequiredCredentials(creds, realm.getRequiredCredentials());
     }
 
     @Override
     public void updateRequiredApplicationCredentials(Set<String> creds) {
-        List<RequiredCredentialData> credsData = getRequiredCredentialsData(RequiredCredentialData.CLIENT_TYPE_RESOURCE);
-        updateRequiredCredentials(creds, credsData);
+        updateRequiredCredentials(creds, realm.getRequiredApplicationCredentials());
     }
 
     @Override
     public void updateRequiredOAuthClientCredentials(Set<String> creds) {
-        List<RequiredCredentialData> credsData = getRequiredCredentialsData(RequiredCredentialData.CLIENT_TYPE_OAUTH_RESOURCE);
-        updateRequiredCredentials(creds, credsData);
+        updateRequiredCredentials(creds, realm.getRequiredOAuthClientCredentials());
     }
 
-    protected void updateRequiredCredentials(Set<String> creds, List<RequiredCredentialData> credsData) {
+    protected void updateRequiredCredentials(Set<String> creds, List<RequiredCredentialEntity> credsEntities) {
         Set<String> already = new HashSet<String>();
-        for (RequiredCredentialData data : credsData) {
-            if (!creds.contains(data.getType())) {
-                noSQL.removeObject(data);
+        Set<RequiredCredentialEntity> toRemove = new HashSet<RequiredCredentialEntity>();
+        for (RequiredCredentialEntity entity : credsEntities) {
+            if (!creds.contains(entity.getType())) {
+                toRemove.add(entity);
             } else {
-                already.add(data.getType());
+                already.add(entity.getType());
             }
         }
+        for (RequiredCredentialEntity entity : toRemove) {
+            credsEntities.remove(entity);
+        }
         for (String cred : creds) {
-            // TODO
-            System.out.println("updating cred: " + cred);
-            // logger.info("updating cred: " + cred);
+            logger.info("updating cred: " + cred);
             if (!already.contains(cred)) {
-                addRequiredCredential(cred);
+                RequiredCredentialModel credentialModel = initRequiredCredentialModel(cred);
+                addRequiredCredential(credentialModel, credsEntities);
             }
         }
     }
 
     @Override
     public List<RequiredCredentialModel> getRequiredCredentials() {
-        return getRequiredCredentials(RequiredCredentialData.CLIENT_TYPE_USER);
+        return convertRequiredCredentialEntities(realm.getRequiredCredentials());
     }
 
     @Override
     public List<RequiredCredentialModel> getRequiredApplicationCredentials() {
-        return getRequiredCredentials(RequiredCredentialData.CLIENT_TYPE_RESOURCE);
+        return convertRequiredCredentialEntities(realm.getRequiredApplicationCredentials());
     }
 
     @Override
     public List<RequiredCredentialModel> getRequiredOAuthClientCredentials() {
-        return getRequiredCredentials(RequiredCredentialData.CLIENT_TYPE_OAUTH_RESOURCE);
+        return convertRequiredCredentialEntities(realm.getRequiredOAuthClientCredentials());
     }
 
-    protected List<RequiredCredentialModel> getRequiredCredentials(int credentialType) {
-        List<RequiredCredentialData> credsData = getRequiredCredentialsData(credentialType);
+    protected List<RequiredCredentialModel> convertRequiredCredentialEntities(Collection<RequiredCredentialEntity> credEntities) {
 
         List<RequiredCredentialModel> result = new ArrayList<RequiredCredentialModel>();
-        for (RequiredCredentialData data : credsData) {
+        for (RequiredCredentialEntity entity : credEntities) {
             RequiredCredentialModel model = new RequiredCredentialModel();
-            model.setFormLabel(data.getFormLabel());
-            model.setInput(data.isInput());
-            model.setSecret(data.isSecret());
-            model.setType(data.getType());
+            model.setFormLabel(entity.getFormLabel());
+            model.setInput(entity.isInput());
+            model.setSecret(entity.isSecret());
+            model.setType(entity.getType());
 
             result.add(model);
         }
         return result;
     }
 
-    protected List<RequiredCredentialData> getRequiredCredentialsData(int credentialType) {
-        NoSQLQuery query = noSQL.createQueryBuilder()
-                .andCondition("realmId", getOid())
-                .andCondition("clientType", credentialType)
-                .build();
-        return noSQL.loadObjects(RequiredCredentialData.class, query);
-    }
-
     @Override
     public boolean validatePassword(UserModel user, String password) {
-        Credentials.Status status = passwordCredentialHandler.validate(noSQL, ((UserAdapter)user).getUser(), password);
-        return status == Credentials.Status.VALID;
+        for (CredentialEntity cred : ((UserAdapter)user).getUser().getCredentials()) {
+            if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
+                return new Pbkdf2PasswordEncoder(cred.getSalt()).verify(password, cred.getValue());
+            }
+        }
+        return false;
     }
 
     @Override
     public boolean validateTOTP(UserModel user, String password, String token) {
-        Credentials.Status status = totpCredentialHandler.validate(noSQL, ((UserAdapter)user).getUser(), password, token, null);
-        return status == Credentials.Status.VALID;
+        if (!validatePassword(user, password)) return false;
+        for (CredentialEntity cred : ((UserAdapter)user).getUser().getCredentials()) {
+            if (cred.getType().equals(UserCredentialModel.TOTP)) {
+                return new TimeBasedOTP().validate(token, cred.getValue().getBytes());
+            }
+        }
+        return false;
     }
 
     @Override
     public void updateCredential(UserModel user, UserCredentialModel cred) {
-        if (cred.getType().equals(CredentialRepresentation.PASSWORD)) {
-            passwordCredentialHandler.update(noSQL, ((UserAdapter)user).getUser(), cred.getValue(), null, null);
-        } else if (cred.getType().equals(CredentialRepresentation.TOTP)) {
-            totpCredentialHandler.update(noSQL, ((UserAdapter)user).getUser(), cred.getValue(), cred.getDevice(), null, null);
-        } else if (cred.getType().equals(CredentialRepresentation.CLIENT_CERT)) {
-            // TODO
-//            X509Certificate cert = null;
-//            try {
-//                cert = org.keycloak.PemUtils.decodeCertificate(cred.getValue());
-//            } catch (Exception e) {
-//                throw new RuntimeException(e);
-//            }
-//            X509CertificateCredentials creds = new X509CertificateCredentials(cert);
-//            idm.updateCredential(((UserAdapter)user).getUser(), creds);
+        CredentialEntity credentialEntity = null;
+        UserEntity userEntity = ((UserAdapter) user).getUser();
+        for (CredentialEntity entity : userEntity.getCredentials()) {
+            if (entity.getType().equals(cred.getType())) {
+                credentialEntity = entity;
+            }
+        }
+
+        if (credentialEntity == null) {
+            credentialEntity = new CredentialEntity();
+            credentialEntity.setType(cred.getType());
+            credentialEntity.setDevice(cred.getDevice());
+            userEntity.getCredentials().add(credentialEntity);
+        }
+        if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
+            byte[] salt = Pbkdf2PasswordEncoder.getSalt();
+            credentialEntity.setValue(new Pbkdf2PasswordEncoder(salt).encode(cred.getValue()));
+            credentialEntity.setSalt(salt);
+        } else {
+            credentialEntity.setValue(cred.getValue());
         }
+        credentialEntity.setDevice(cred.getDevice());
+
+        getMongoStore().updateEntity(userEntity, invocationContext);
     }
 
     @Override
     public UserModel getUserBySocialLink(SocialLinkModel socialLink) {
-        NoSQLQuery query = noSQL.createQueryBuilder()
-                .andCondition("socialProvider", socialLink.getSocialProvider())
-                .andCondition("socialUsername", socialLink.getSocialUsername())
-                .andCondition("realmId", getOid())
-                .build();
-        SocialLinkData socialLinkData = noSQL.loadSingleObject(SocialLinkData.class, query);
-
-        if (socialLinkData == null) {
-            return null;
-        } else {
-            UserData userData = noSQL.loadObject(UserData.class, socialLinkData.getUserId());
-            // TODO: Add some checking if userData exists and programmatically remove binding if it doesn't? (There are more similar places where this should be handled)
-            return new UserAdapter(userData, noSQL);
-        }
+        DBObject query = new QueryBuilder()
+                .and("socialLinks.socialProvider").is(socialLink.getSocialProvider())
+                .and("socialLinks.socialUsername").is(socialLink.getSocialUsername())
+                .and("realmId").is(getId())
+                .get();
+        UserEntity userEntity = getMongoStore().loadSingleEntity(UserEntity.class, query, invocationContext);
+        return userEntity==null ? null : new UserAdapter(userEntity, invocationContext);
     }
 
     @Override
     public Set<SocialLinkModel> getSocialLinks(UserModel user) {
-        UserData userData = ((UserAdapter)user).getUser();
-        String userId = userData.getId();
+        UserEntity userEntity = ((UserAdapter)user).getUser();
+        List<SocialLinkEntity> linkEntities = userEntity.getSocialLinks();
 
-        NoSQLQuery query = noSQL.createQueryBuilder()
-                .andCondition("userId", userId)
-                .build();
-        List<SocialLinkData> dbSocialLinks = noSQL.loadObjects(SocialLinkData.class, query);
+        if (linkEntities == null) {
+            return Collections.EMPTY_SET;
+        }
 
         Set<SocialLinkModel> result = new HashSet<SocialLinkModel>();
-        for (SocialLinkData socialLinkData : dbSocialLinks) {
-            SocialLinkModel model = new SocialLinkModel(socialLinkData.getSocialProvider(), socialLinkData.getSocialUsername());
+        for (SocialLinkEntity socialLinkEntity : linkEntities) {
+            SocialLinkModel model = new SocialLinkModel(socialLinkEntity.getSocialProvider(), socialLinkEntity.getSocialUsername());
             result.add(model);
         }
         return result;
@@ -785,30 +827,26 @@ public class RealmAdapter implements RealmModel {
 
     @Override
     public void addSocialLink(UserModel user, SocialLinkModel socialLink) {
-        UserData userData = ((UserAdapter)user).getUser();
-        SocialLinkData socialLinkData = new SocialLinkData();
-        socialLinkData.setSocialProvider(socialLink.getSocialProvider());
-        socialLinkData.setSocialUsername(socialLink.getSocialUsername());
-        socialLinkData.setUserId(userData.getId());
-        socialLinkData.setRealmId(getOid());
+        UserEntity userEntity = ((UserAdapter)user).getUser();
+        SocialLinkEntity socialLinkEntity = new SocialLinkEntity();
+        socialLinkEntity.setSocialProvider(socialLink.getSocialProvider());
+        socialLinkEntity.setSocialUsername(socialLink.getSocialUsername());
 
-        noSQL.saveObject(socialLinkData);
+        getMongoStore().pushItemToList(userEntity, "socialLinks", socialLinkEntity, true, invocationContext);
     }
 
     @Override
     public void removeSocialLink(UserModel user, SocialLinkModel socialLink) {
-        UserData userData = ((UserAdapter)user).getUser();
-        String userId = userData.getId();
-        NoSQLQuery query = noSQL.createQueryBuilder()
-                .andCondition("socialProvider", socialLink.getSocialProvider())
-                .andCondition("socialUsername", socialLink.getSocialUsername())
-                .andCondition("userId", userId)
-                .build();
-        noSQL.removeObjects(SocialLinkData.class, query);
+        SocialLinkEntity socialLinkEntity = new SocialLinkEntity();
+        socialLinkEntity.setSocialProvider(socialLink.getSocialProvider());
+        socialLinkEntity.setSocialUsername(socialLink.getSocialUsername());
+
+        UserEntity userEntity = ((UserAdapter)user).getUser();
+        getMongoStore().pullItemFromList(userEntity, "socialLinks", socialLinkEntity, invocationContext);
     }
 
     protected void updateRealm() {
-        noSQL.saveObject(realm);
+        getMongoStore().updateEntity(realm, invocationContext);
     }
 
     protected RequiredCredentialModel initRequiredCredentialModel(String type) {
@@ -820,46 +858,108 @@ public class RealmAdapter implements RealmModel {
     }
 
     @Override
+    public List<UserModel> getUsers() {
+        DBObject query = new QueryBuilder()
+                .and("realmId").is(getId())
+                .get();
+        List<UserEntity> users = getMongoStore().loadEntities(UserEntity.class, query, invocationContext);
+        return convertUserEntities(users);
+    }
+
+    @Override
+    public List<UserModel> searchForUser(String search) {
+        search = search.trim();
+        Pattern caseInsensitivePattern = Pattern.compile("(?i:" + search + ")");
+
+        QueryBuilder nameBuilder;
+        int spaceInd = search.lastIndexOf(" ");
+
+        // Case when we have search string like "ohn Bow". Then firstName must end with "ohn" AND lastName must start with "bow" (everything case-insensitive)
+        if (spaceInd != -1) {
+            String firstName = search.substring(0, spaceInd);
+            String lastName = search.substring(spaceInd + 1);
+            Pattern firstNamePattern =  Pattern.compile("(?i:" + firstName + "$)");
+            Pattern lastNamePattern =  Pattern.compile("(?i:^" + lastName + ")");
+            nameBuilder = new QueryBuilder().and(
+                    new QueryBuilder().put("firstName").regex(firstNamePattern).get(),
+                    new QueryBuilder().put("lastName").regex(lastNamePattern).get()
+            );
+        } else {
+            // Case when we have search without spaces like "foo". The firstName OR lastName could be "foo" (everything case-insensitive)
+            nameBuilder = new QueryBuilder().or(
+                    new QueryBuilder().put("firstName").regex(caseInsensitivePattern).get(),
+                    new QueryBuilder().put("lastName").regex(caseInsensitivePattern).get()
+            );
+        }
+
+        QueryBuilder builder = new QueryBuilder().and(
+                new QueryBuilder().and("realmId").is(getId()).get(),
+                new QueryBuilder().or(
+                        new QueryBuilder().put("loginName").regex(caseInsensitivePattern).get(),
+                        new QueryBuilder().put("email").regex(caseInsensitivePattern).get(),
+                        nameBuilder.get()
+
+                ).get()
+        );
+
+        List<UserEntity> users = getMongoStore().loadEntities(UserEntity.class, builder.get(), invocationContext);
+        return convertUserEntities(users);
+    }
+
+    @Override
     public List<UserModel> searchForUserByAttributes(Map<String, String> attributes) {
-        NoSQLQueryBuilder queryBuilder = noSQL.createQueryBuilder();
+        QueryBuilder queryBuilder = new QueryBuilder()
+                .and("realmId").is(getId());
+
         for (Map.Entry<String, String> entry : attributes.entrySet()) {
             if (entry.getKey().equals(UserModel.LOGIN_NAME)) {
-                queryBuilder.andCondition("loginName", entry.getValue());
+                queryBuilder.and("loginName").regex(Pattern.compile("(?i:" + entry.getValue() + "$)"));
             } else if (entry.getKey().equalsIgnoreCase(UserModel.FIRST_NAME)) {
-                queryBuilder.andCondition(UserModel.FIRST_NAME, entry.getValue());
+                queryBuilder.and(UserModel.FIRST_NAME).regex(Pattern.compile("(?i:" + entry.getValue() + "$)"));
 
             } else if (entry.getKey().equalsIgnoreCase(UserModel.LAST_NAME)) {
-                queryBuilder.andCondition(UserModel.LAST_NAME, entry.getValue());
+                queryBuilder.and(UserModel.LAST_NAME).regex(Pattern.compile("(?i:" + entry.getValue() + "$)"));
 
             } else if (entry.getKey().equalsIgnoreCase(UserModel.EMAIL)) {
-                queryBuilder.andCondition(UserModel.EMAIL, entry.getValue());
+                queryBuilder.and(UserModel.EMAIL).regex(Pattern.compile("(?i:" + entry.getValue() + "$)"));
             }
         }
-        List<UserData> users = noSQL.loadObjects(UserData.class, queryBuilder.build());
+        List<UserEntity> users = getMongoStore().loadEntities(UserEntity.class, queryBuilder.get(), invocationContext);
+        return convertUserEntities(users);
+    }
+
+    protected List<UserModel> convertUserEntities(List<UserEntity> userEntities) {
         List<UserModel> userModels = new ArrayList<UserModel>();
-        for (UserData user : users) {
-            userModels.add(new UserAdapter(user, noSQL));
+        for (UserEntity user : userEntities) {
+            userModels.add(new UserAdapter(user, invocationContext));
         }
         return userModels;
     }
 
     @Override
     public Map<String, String> getSmtpConfig() {
-        throw new RuntimeException("Not implemented");
+        return realm.getSmtpConfig();
     }
 
     @Override
     public void setSmtpConfig(Map<String, String> smtpConfig) {
-        throw new RuntimeException("Not implemented");
+        realm.setSmtpConfig(smtpConfig);
+        updateRealm();
     }
 
     @Override
     public Map<String, String> getSocialConfig() {
-        throw new RuntimeException("Not implemented");
+        return realm.getSocialConfig();
     }
 
     @Override
     public void setSocialConfig(Map<String, String> socialConfig) {
-        throw new RuntimeException("Not implemented");
+        realm.setSocialConfig(socialConfig);
+        updateRealm();
+    }
+
+    @Override
+    public AbstractMongoIdentifiableEntity getMongoEntity() {
+        return realm;
     }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java
index 7b2692f..a3ed40b 100644
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java
@@ -1,22 +1,45 @@
 package org.keycloak.models.mongo.keycloak.adapters;
 
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.mongodb.DBObject;
+import com.mongodb.QueryBuilder;
+import org.keycloak.models.RoleContainerModel;
 import org.keycloak.models.RoleModel;
-import org.keycloak.models.mongo.api.NoSQL;
-import org.keycloak.models.mongo.keycloak.data.RoleData;
+import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.mongo.keycloak.entities.ApplicationEntity;
+import org.keycloak.models.mongo.keycloak.entities.RealmEntity;
+import org.keycloak.models.mongo.keycloak.entities.RoleEntity;
+import org.keycloak.models.mongo.utils.MongoModelUtils;
+import org.keycloak.models.utils.KeycloakModelUtils;
 
 /**
  * Wrapper around RoleData object, which will persist wrapped object after each set operation (compatibility with picketlink based impl)
  *
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
-public class RoleAdapter implements RoleModel {
+public class RoleAdapter extends AbstractAdapter implements RoleModel {
+
+    private final RoleEntity role;
+    private RoleContainerModel roleContainer;
 
-    private final RoleData role;
-    private final NoSQL noSQL;
+    public RoleAdapter(RoleEntity roleEntity, MongoStoreInvocationContext invContext) {
+        this(roleEntity, null, invContext);
+    }
+
+    public RoleAdapter(RoleEntity roleEntity, RoleContainerModel roleContainer, MongoStoreInvocationContext invContext) {
+        super(invContext);
+        this.role = roleEntity;
+        this.roleContainer = roleContainer;
+    }
 
-    public RoleAdapter(RoleData roleData, NoSQL noSQL) {
-        this.role = roleData;
-        this.noSQL = noSQL;
+    @Override
+    public String getId() {
+        return role.getId();
     }
 
     @Override
@@ -25,6 +48,12 @@ public class RoleAdapter implements RoleModel {
     }
 
     @Override
+    public void setName(String name) {
+        role.setName(name);
+        updateRole();
+    }
+
+    @Override
     public String getDescription() {
         return role.getDescription();
     }
@@ -32,21 +61,84 @@ public class RoleAdapter implements RoleModel {
     @Override
     public void setDescription(String description) {
         role.setDescription(description);
-        noSQL.saveObject(role);
+        updateRole();
     }
 
     @Override
-    public String getId() {
-        return role.getId();
+    public boolean isComposite() {
+        return role.getCompositeRoleIds() != null && role.getCompositeRoleIds().size() > 0;
+    }
+
+    protected void updateRole() {
+        getMongoStore().updateEntity(role, invocationContext);
     }
 
     @Override
-    public void setName(String name) {
-        role.setName(name);
-        noSQL.saveObject(role);
+    public void addCompositeRole(RoleModel childRole) {
+        getMongoStore().pushItemToList(role, "compositeRoleIds", childRole.getId(), true, invocationContext);
     }
 
-    public RoleData getRole() {
+    @Override
+    public void removeCompositeRole(RoleModel childRole) {
+        getMongoStore().pullItemFromList(role, "compositeRoleIds", childRole.getId(), invocationContext);
+    }
+
+    @Override
+    public Set<RoleModel> getComposites() {
+        if (role.getCompositeRoleIds() == null || role.getCompositeRoleIds().isEmpty()) {
+            return Collections.EMPTY_SET;
+        }
+
+        DBObject query = new QueryBuilder()
+                .and("_id").in(MongoModelUtils.convertStringsToObjectIds(role.getCompositeRoleIds()))
+                .get();
+        List<RoleEntity> childRoles = getMongoStore().loadEntities(RoleEntity.class, query, invocationContext);
+
+        Set<RoleModel> set = new HashSet<RoleModel>();
+        for (RoleEntity childRole : childRoles) {
+            set.add(new RoleAdapter(childRole, invocationContext));
+        }
+        return set;
+    }
+
+    @Override
+    public RoleContainerModel getContainer() {
+        if (roleContainer == null) {
+            // Compute it
+            if (role.getRealmId() != null) {
+                RealmEntity realm = getMongoStore().loadEntity(RealmEntity.class, role.getRealmId(), invocationContext);
+                if (realm == null) {
+                    throw new IllegalStateException("Realm with id: " + role.getRealmId() + " doesn't exists");
+                }
+                roleContainer = new RealmAdapter(realm, invocationContext);
+            } else if (role.getApplicationId() != null) {
+                ApplicationEntity appEntity = getMongoStore().loadEntity(ApplicationEntity.class, role.getApplicationId(), invocationContext);
+                if (appEntity == null) {
+                    throw new IllegalStateException("Application with id: " + role.getApplicationId() + " doesn't exists");
+                }
+                roleContainer = new ApplicationAdapter(appEntity, invocationContext);
+            } else {
+                throw new IllegalStateException("Both realmId and applicationId are null for role: " + this);
+            }
+        }
+        return roleContainer;
+    }
+
+    @Override
+    public boolean hasRole(RoleModel role) {
+        if (this.equals(role)) return true;
+        if (!isComposite()) return false;
+
+        Set<RoleModel> visited = new HashSet<RoleModel>();
+        return KeycloakModelUtils.searchFor(role, this, visited);
+    }
+
+    public RoleEntity getRole() {
+        return role;
+    }
+
+    @Override
+    public AbstractMongoIdentifiableEntity getMongoEntity() {
         return role;
     }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
index 3b20848..1a18387 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
@@ -1,10 +1,13 @@
 package org.keycloak.models.mongo.keycloak.adapters;
 
 import org.keycloak.models.UserModel;
-import org.keycloak.models.mongo.api.NoSQL;
-import org.keycloak.models.mongo.keycloak.data.UserData;
+import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.mongo.keycloak.entities.UserEntity;
 
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -15,14 +18,13 @@ import java.util.Set;
  *
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
-public class UserAdapter implements UserModel {
+public class UserAdapter extends AbstractAdapter implements UserModel {
 
-    private final UserData user;
-    private final NoSQL noSQL;
+    private final UserEntity user;
 
-    public UserAdapter(UserData userData, NoSQL noSQL) {
-        this.user = userData;
-        this.noSQL = noSQL;
+    public UserAdapter(UserEntity userEntity, MongoStoreInvocationContext invContext) {
+        super(invContext);
+        this.user = userEntity;
     }
 
     @Override
@@ -38,7 +40,7 @@ public class UserAdapter implements UserModel {
     @Override
     public void setEnabled(boolean enabled) {
         user.setEnabled(enabled);
-        noSQL.saveObject(user);
+        updateUser();
     }
 
     @Override
@@ -49,7 +51,7 @@ public class UserAdapter implements UserModel {
     @Override
     public void setFirstName(String firstName) {
         user.setFirstName(firstName);
-        noSQL.saveObject(user);
+        updateUser();
     }
 
     @Override
@@ -60,7 +62,7 @@ public class UserAdapter implements UserModel {
     @Override
     public void setLastName(String lastName) {
         user.setLastName(lastName);
-        noSQL.saveObject(user);
+        updateUser();
     }
 
     @Override
@@ -71,7 +73,7 @@ public class UserAdapter implements UserModel {
     @Override
     public void setEmail(String email) {
         user.setEmail(email);
-        noSQL.saveObject(user);
+        updateUser();
     }
 
     @Override
@@ -82,111 +84,131 @@ public class UserAdapter implements UserModel {
     @Override
     public void setEmailVerified(boolean verified) {
         user.setEmailVerified(verified);
-        noSQL.saveObject(user);
+        updateUser();
     }
 
     @Override
     public void setAttribute(String name, String value) {
-        user.setAttribute(name, value);
+        if (user.getAttributes() == null) {
+            user.setAttributes(new HashMap<String, String>());
+        }
+
+        user.getAttributes().put(name, value);
+        updateUser();
     }
 
     @Override
     public void removeAttribute(String name) {
-        user.removeAttribute(name);
-        noSQL.saveObject(user);
+        if (user.getAttributes() == null) return;
+
+        user.getAttributes().remove(name);
+        updateUser();
     }
 
     @Override
     public String getAttribute(String name) {
-        return user.getAttribute(name);
+        return user.getAttributes()==null ? null : user.getAttributes().get(name);
     }
 
     @Override
     public Map<String, String> getAttributes() {
-        return user.getAttributes();
+        return user.getAttributes()==null ? Collections.EMPTY_MAP : Collections.unmodifiableMap(user.getAttributes());
     }
 
-    public UserData getUser() {
+    public UserEntity getUser() {
         return user;
     }
 
     @Override
-    public Set<RequiredAction> getRequiredActions() {
-        List<RequiredAction> actions = user.getRequiredActions();
-
-        // Compatibility with picketlink impl
-        if (actions == null) {
-            return Collections.emptySet();
-        } else {
-            Set<RequiredAction> s = new HashSet<RequiredAction>();
-            for (RequiredAction a : actions) {
-                s.add(a);
-            }
-            return Collections.unmodifiableSet(s);
+    public Set<String> getWebOrigins() {
+        Set<String> result = new HashSet<String>();
+        if (user.getWebOrigins() != null) {
+            result.addAll(user.getWebOrigins());
         }
+        return result;
     }
 
     @Override
-    public void addRequiredAction(RequiredAction action) {
-        // Push action only if it's not already here
-        if (user.getRequiredActions() == null || !user.getRequiredActions().contains(action)) {
-            noSQL.pushItemToList(user, "requiredActions", action);
-        }
+    public void setWebOrigins(Set<String> webOrigins) {
+        List<String> result = new ArrayList<String>();
+        result.addAll(webOrigins);
+        user.setWebOrigins(result);
+        updateUser();
     }
 
     @Override
-    public void removeRequiredAction(RequiredAction action) {
-        noSQL.pullItemFromList(user, "requiredActions", action);
+    public void addWebOrigin(String webOrigin) {
+        getMongoStore().pushItemToList(user, "webOrigins", webOrigin, true, invocationContext);
     }
 
     @Override
-    public boolean isTotp() {
-        return user.isTotp();
+    public void removeWebOrigin(String webOrigin) {
+        getMongoStore().pullItemFromList(user, "webOrigins", webOrigin, invocationContext);
     }
 
     @Override
-    public void setTotp(boolean totp) {
-        user.setTotp(totp);
-        noSQL.saveObject(user);
+    public Set<String> getRedirectUris() {
+        Set<String> result = new HashSet<String>();
+        if (user.getRedirectUris() != null) {
+            result.addAll(user.getRedirectUris());
+        }
+        return result;
     }
 
     @Override
-    public Set<String> getWebOrigins() {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public void setRedirectUris(Set<String> redirectUris) {
+        List<String> result = new ArrayList<String>();
+        result.addAll(redirectUris);
+        user.setRedirectUris(result);
+        updateUser();
     }
 
     @Override
-    public void setWebOrigins(Set<String> webOrigins) {
-        //To change body of implemented methods use File | Settings | File Templates.
+    public void addRedirectUri(String redirectUri) {
+        getMongoStore().pushItemToList(user, "redirectUris", redirectUri, true, invocationContext);
     }
 
     @Override
-    public void addWebOrigin(String webOrigin) {
-        //To change body of implemented methods use File | Settings | File Templates.
+    public void removeRedirectUri(String redirectUri) {
+        getMongoStore().pullItemFromList(user, "redirectUris", redirectUri, invocationContext);
     }
 
     @Override
-    public void removeWebOrigin(String webOrigin) {
-        //To change body of implemented methods use File | Settings | File Templates.
+    public Set<RequiredAction> getRequiredActions() {
+        Set<RequiredAction> result = new HashSet<RequiredAction>();
+        if (user.getRequiredActions() != null) {
+            result.addAll(user.getRequiredActions());
+        }
+        return result;
     }
 
     @Override
-    public Set<String> getRedirectUris() {
-        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    public void addRequiredAction(RequiredAction action) {
+        getMongoStore().pushItemToList(user, "requiredActions", action, true, invocationContext);
     }
 
     @Override
-    public void setRedirectUris(Set<String> redirectUris) {
-        //To change body of implemented methods use File | Settings | File Templates.
+    public void removeRequiredAction(RequiredAction action) {
+        getMongoStore().pullItemFromList(user, "requiredActions", action, invocationContext);
     }
 
     @Override
-    public void addRedirectUri(String redirectUri) {
-        //To change body of implemented methods use File | Settings | File Templates.
+    public boolean isTotp() {
+        return user.isTotp();
     }
 
     @Override
-    public void removeRedirectUri(String redirectUri) {
-        //To change body of implemented methods use File | Settings | File Templates.
+    public void setTotp(boolean totp) {
+        user.setTotp(totp);
+        updateUser();
+    }
+
+    protected void updateUser() {
+        getMongoStore().updateEntity(user, invocationContext);
+    }
+
+    @Override
+    public AbstractMongoIdentifiableEntity getMongoEntity() {
+        return user;
     }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/CredentialEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/CredentialEntity.java
new file mode 100644
index 0000000..6ab322d
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/CredentialEntity.java
@@ -0,0 +1,51 @@
+package org.keycloak.models.mongo.keycloak.entities;
+
+import org.keycloak.models.mongo.api.MongoEntity;
+import org.keycloak.models.mongo.api.MongoField;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class CredentialEntity implements MongoEntity {
+
+    private String type;
+    private String value;
+    private String device;
+    private byte[] salt;
+
+    @MongoField
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    @MongoField
+    public String getValue() {
+        return value;
+    }
+
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    @MongoField
+    public String getDevice() {
+        return device;
+    }
+
+    public void setDevice(String device) {
+        this.device = device;
+    }
+
+    @MongoField
+    public byte[] getSalt() {
+        return salt;
+    }
+
+    public void setSalt(byte[] salt) {
+        this.salt = salt;
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RoleEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RoleEntity.java
new file mode 100755
index 0000000..e2e9727
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/RoleEntity.java
@@ -0,0 +1,131 @@
+package org.keycloak.models.mongo.keycloak.entities;
+
+import com.mongodb.DBObject;
+import com.mongodb.QueryBuilder;
+import org.jboss.logging.Logger;
+import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
+import org.keycloak.models.mongo.api.MongoCollection;
+import org.keycloak.models.mongo.api.MongoEntity;
+import org.keycloak.models.mongo.api.MongoField;
+import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@MongoCollection(collectionName = "roles")
+public class RoleEntity extends AbstractMongoIdentifiableEntity implements MongoEntity {
+
+    private static final Logger logger = Logger.getLogger(RoleEntity.class);
+
+    private String name;
+    private String description;
+
+    private List<String> compositeRoleIds;
+
+    private String realmId;
+    private String applicationId;
+
+    @MongoField
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @MongoField
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    @MongoField
+    public List<String> getCompositeRoleIds() {
+        return compositeRoleIds;
+    }
+
+    public void setCompositeRoleIds(List<String> compositeRoleIds) {
+        this.compositeRoleIds = compositeRoleIds;
+    }
+
+    @MongoField
+    public String getRealmId() {
+        return realmId;
+    }
+
+    public void setRealmId(String realmId) {
+        this.realmId = realmId;
+    }
+
+    @MongoField
+    public String getApplicationId() {
+        return applicationId;
+    }
+
+    public void setApplicationId(String applicationId) {
+        this.applicationId = applicationId;
+    }
+
+    @Override
+    public void afterRemove(MongoStoreInvocationContext invContext) {
+        MongoStore mongoStore = invContext.getMongoStore();
+
+        // Remove this role from all users, which has it
+        DBObject query = new QueryBuilder()
+                .and("roleIds").is(getId())
+                .get();
+
+        List<UserEntity> users = mongoStore.loadEntities(UserEntity.class, query, invContext);
+        for (UserEntity user : users) {
+            logger.info("Removing role " + getName() + " from user " + user.getLoginName());
+            mongoStore.pullItemFromList(user, "roleIds", getId(), invContext);
+        }
+
+        // Remove this scope from all users, which has it
+        query = new QueryBuilder()
+                .and("scopeIds").is(getId())
+                .get();
+
+        users = mongoStore.loadEntities(UserEntity.class, query, invContext);
+        for (UserEntity user : users) {
+            logger.info("Removing scope " + getName() + " from user " + user.getLoginName());
+            mongoStore.pullItemFromList(user, "scopeIds", getId(), invContext);
+        }
+
+        // Remove defaultRoles from realm
+        if (realmId != null) {
+            RealmEntity realmEntity = mongoStore.loadEntity(RealmEntity.class, realmId, invContext);
+
+            // Realm might be already removed at this point
+            if (realmEntity != null) {
+                mongoStore.pullItemFromList(realmEntity, "defaultRoles", getId(), invContext);
+            }
+        }
+
+        // Remove defaultRoles from application
+        if (applicationId != null) {
+            ApplicationEntity appEntity = mongoStore.loadEntity(ApplicationEntity.class, applicationId, invContext);
+
+            // Application might be already removed at this point
+            if (appEntity != null) {
+                mongoStore.pullItemFromList(appEntity, "defaultRoles", getId(), invContext);
+            }
+        }
+
+        // Remove this role from others who has it as composite
+        query = new QueryBuilder()
+                .and("compositeRoleIds").is(getId())
+                .get();
+        List<RoleEntity> parentRoles = mongoStore.loadEntities(RoleEntity.class, query, invContext);
+        for (RoleEntity role : parentRoles) {
+            mongoStore.pullItemFromList(role, "compositeRoleIds", getId(), invContext);
+        }
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/SocialLinkEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/SocialLinkEntity.java
new file mode 100644
index 0000000..85ae5c0
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/SocialLinkEntity.java
@@ -0,0 +1,58 @@
+package org.keycloak.models.mongo.keycloak.entities;
+
+import org.keycloak.models.mongo.api.MongoEntity;
+import org.keycloak.models.mongo.api.MongoField;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class SocialLinkEntity implements MongoEntity {
+
+    private String socialUsername;
+    private String socialProvider;
+
+    @MongoField
+    public String getSocialUsername() {
+        return socialUsername;
+    }
+
+    public void setSocialUsername(String socialUsername) {
+        this.socialUsername = socialUsername;
+    }
+
+    @MongoField
+    public String getSocialProvider() {
+        return socialProvider;
+    }
+
+    public void setSocialProvider(String socialProvider) {
+        this.socialProvider = socialProvider;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        SocialLinkEntity that = (SocialLinkEntity) o;
+
+        if (socialProvider != null && (that.socialProvider == null || !socialProvider.equals(that.socialProvider))) return false;
+        if (socialUsername != null && (that.socialUsername == null || !socialUsername.equals(that.socialUsername))) return false;
+        if (socialProvider == null && that.socialProvider != null)return false;
+        if (socialUsername == null && that.socialUsername != null) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int code = 1;
+        if (socialUsername != null) {
+            code = code * 13;
+        }
+        if (socialProvider != null) {
+            code = code * 17;
+        }
+        return code;
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/MongoModelProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/MongoModelProvider.java
index c035b56..910adde 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/MongoModelProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/MongoModelProvider.java
@@ -2,6 +2,9 @@ package org.keycloak.models.mongo.keycloak;
 
 import org.keycloak.models.KeycloakSessionFactory;
 import org.keycloak.models.ModelProvider;
+import org.keycloak.models.mongo.keycloak.adapters.MongoKeycloakSessionFactory;
+import org.keycloak.models.mongo.utils.MongoConfiguration;
+import org.keycloak.models.mongo.utils.SystemPropertiesConfigurationProvider;
 
 import java.lang.Override;
 
@@ -18,16 +21,7 @@ public class MongoModelProvider implements ModelProvider {
 
     @Override
     public KeycloakSessionFactory createFactory() {
-            String host = PropertiesManager.getMongoHost();
-            int port = PropertiesManager.getMongoPort();
-            String dbName = PropertiesManager.getMongoDbName();
-            boolean dropDatabaseOnStartup = PropertiesManager.dropDatabaseOnStartup();
-
-            // Create MongoDBSessionFactory via reflection now
-            try {
-                return new MongoDBSessionFactory(host, port, dbName, dropDatabaseOnStartup);
-            } catch (Exception e) {
-                throw new RuntimeException(e);
-            }
+        MongoConfiguration config = SystemPropertiesConfigurationProvider.createConfiguration();
+        return new MongoKeycloakSessionFactory(config);
     }
 }
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoConfiguration.java b/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoConfiguration.java
new file mode 100644
index 0000000..7ba22d1
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoConfiguration.java
@@ -0,0 +1,44 @@
+package org.keycloak.models.mongo.utils;
+
+/**
+ * Encapsulates all info about configuration of MongoDB instance
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MongoConfiguration {
+
+    private final String host;
+    private final int port;
+    private final String dbName;
+
+    private final boolean clearCollectionsOnStartup;
+
+    public MongoConfiguration(String host, int port, String dbName, boolean clearCollectionsOnStartup) {
+        this.host = host;
+        this.port = port;
+        this.dbName = dbName;
+        this.clearCollectionsOnStartup = clearCollectionsOnStartup;
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public String getDbName() {
+        return dbName;
+    }
+
+    public boolean isClearCollectionsOnStartup() {
+        return clearCollectionsOnStartup;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("MongoConfiguration: host: %s, port: %d, dbName: %s, clearCollectionsOnStartup: %b",
+                host, port, dbName, clearCollectionsOnStartup);
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java b/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java
new file mode 100644
index 0000000..4be29ec
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/utils/MongoModelUtils.java
@@ -0,0 +1,59 @@
+package org.keycloak.models.mongo.utils;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import com.mongodb.DBObject;
+import com.mongodb.QueryBuilder;
+import org.bson.types.ObjectId;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.mongo.keycloak.adapters.UserAdapter;
+import org.keycloak.models.mongo.keycloak.entities.RoleEntity;
+import org.keycloak.models.mongo.keycloak.entities.UserEntity;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MongoModelUtils {
+
+    public static List<ObjectId> convertStringsToObjectIds(Collection<String> strings) {
+        List<ObjectId> result = new ArrayList<ObjectId>();
+        for (String id : strings) {
+            result.add(new ObjectId(id));
+        }
+        return result;
+    }
+
+    // Get everything including both application and realm roles
+    public static List<RoleEntity> getAllRolesOfUser(UserModel user, MongoStoreInvocationContext invContext) {
+        UserEntity userEntity = ((UserAdapter)user).getUser();
+        List<String> roleIds = userEntity.getRoleIds();
+
+        if (roleIds == null || roleIds.isEmpty()) {
+            return Collections.EMPTY_LIST;
+        }
+
+        DBObject query = new QueryBuilder()
+                .and("_id").in(convertStringsToObjectIds(roleIds))
+                .get();
+        return invContext.getMongoStore().loadEntities(RoleEntity.class, query, invContext);
+    }
+
+    // Get everything including both application and realm scopes
+    public static List<RoleEntity> getAllScopesOfUser(UserModel user, MongoStoreInvocationContext invContext) {
+        UserEntity userEntity = ((UserAdapter)user).getUser();
+        List<String> scopeIds = userEntity.getScopeIds();
+
+        if (scopeIds == null || scopeIds.isEmpty()) {
+            return Collections.EMPTY_LIST;
+        }
+
+        DBObject query = new QueryBuilder()
+                .and("_id").in(convertStringsToObjectIds(scopeIds))
+                .get();
+        return invContext.getMongoStore().loadEntities(RoleEntity.class, query, invContext);
+    }
+}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/utils/SystemPropertiesConfigurationProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/utils/SystemPropertiesConfigurationProvider.java
new file mode 100755
index 0000000..ce24c63
--- /dev/null
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/utils/SystemPropertiesConfigurationProvider.java
@@ -0,0 +1,55 @@
+package org.keycloak.models.mongo.utils;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class SystemPropertiesConfigurationProvider {
+
+    private static final String MONGO_HOST = "keycloak.mongo.host";
+    private static final String MONGO_PORT = "keycloak.mongo.port";
+    private static final String MONGO_DB_NAME = "keycloak.mongo.db";
+    private static final String MONGO_CLEAR_ON_STARTUP = "keycloak.mongo.clearOnStartup";
+
+    // Property names from Liveoak . Those are used as fallback in case that original value is not available
+    private static final String MONGO_HOST_2 = "mongo.host";
+    private static final String MONGO_PORT_2 = "mongo.port";
+    private static final String MONGO_DB_NAME_2 = "mongo.db";
+    private static final String MONGO_CLEAR_ON_STARTUP_2 = "mongo.clearCollectionsOnStartup";
+
+    // Port where MongoDB instance is normally started on linux. This port should be used if we're not starting embedded instance
+    private static final String MONGO_DEFAULT_PORT = "27017";
+
+    public static String getMongoHost() {
+        return getSystemPropertyWithFallback(MONGO_HOST, MONGO_HOST_2, "localhost");
+    }
+
+    public static int getMongoPort() {
+        String portProp = getSystemPropertyWithFallback(MONGO_PORT, MONGO_PORT_2, MONGO_DEFAULT_PORT);
+        return Integer.parseInt(portProp);
+    }
+
+    public static String getMongoDbName() {
+        return getSystemPropertyWithFallback(MONGO_DB_NAME, MONGO_DB_NAME_2, "keycloak");
+    }
+
+    public static boolean isClearCollectionsOnStartup() {
+        String property = getSystemPropertyWithFallback(MONGO_CLEAR_ON_STARTUP, MONGO_CLEAR_ON_STARTUP_2, "false");
+        return "true".equalsIgnoreCase(property);
+    }
+
+    // Check if property propName1 (like "keycloak.mongo.host" is available and if not, then fallback to property "mongo.host" )
+    private static String getSystemPropertyWithFallback(String propName1, String propName2, String defaultValue) {
+        String propValue1 = System.getProperty(propName1);
+        return propValue1!=null ? propValue1 : System.getProperty(propName2, defaultValue);
+    }
+
+    // Create configuration based on system properties
+    public static MongoConfiguration createConfiguration() {
+        return new MongoConfiguration(
+                getMongoHost(),
+                getMongoPort(),
+                getMongoDbName(),
+                isClearCollectionsOnStartup()
+        );
+    }
+}
diff --git a/model/mongo/src/test/java/org/keycloak/models/mongo/test/Address.java b/model/mongo/src/test/java/org/keycloak/models/mongo/test/Address.java
index 386ca31..81bd7d8 100755
--- a/model/mongo/src/test/java/org/keycloak/models/mongo/test/Address.java
+++ b/model/mongo/src/test/java/org/keycloak/models/mongo/test/Address.java
@@ -1,20 +1,20 @@
 package org.keycloak.models.mongo.test;
 
-import org.keycloak.models.mongo.api.AbstractNoSQLObject;
-import org.keycloak.models.mongo.api.NoSQLField;
+import org.keycloak.models.mongo.api.MongoEntity;
+import org.keycloak.models.mongo.api.MongoField;
 
 import java.util.List;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
-public class Address extends AbstractNoSQLObject {
+public class Address implements MongoEntity {
 
     private String street;
     private int number;
     private List<String> flatNumbers;
 
-    @NoSQLField
+    @MongoField
     public String getStreet() {
         return street;
     }
@@ -23,7 +23,7 @@ public class Address extends AbstractNoSQLObject {
         this.street = street;
     }
 
-    @NoSQLField
+    @MongoField
     public int getNumber() {
         return number;
     }
@@ -32,7 +32,7 @@ public class Address extends AbstractNoSQLObject {
         this.number = number;
     }
 
-    @NoSQLField
+    @MongoField
     public List<String> getFlatNumbers() {
         return flatNumbers;
     }
diff --git a/model/mongo/src/test/java/org/keycloak/models/mongo/test/MongoDBModelTest.java b/model/mongo/src/test/java/org/keycloak/models/mongo/test/MongoDBModelTest.java
index 3b8651a..7ee8141 100755
--- a/model/mongo/src/test/java/org/keycloak/models/mongo/test/MongoDBModelTest.java
+++ b/model/mongo/src/test/java/org/keycloak/models/mongo/test/MongoDBModelTest.java
@@ -1,14 +1,19 @@
 package org.keycloak.models.mongo.test;
 
 import com.mongodb.DB;
+import com.mongodb.DBObject;
 import com.mongodb.MongoClient;
+import com.mongodb.QueryBuilder;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
-import org.keycloak.models.mongo.api.NoSQL;
-import org.keycloak.models.mongo.api.NoSQLObject;
-import org.keycloak.models.mongo.api.query.NoSQLQuery;
-import org.keycloak.models.mongo.impl.MongoDBImpl;
+import org.junit.Test;
+import org.keycloak.models.mongo.api.MongoEntity;
+import org.keycloak.models.mongo.api.MongoStore;
+import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
+import org.keycloak.models.mongo.impl.MongoStoreImpl;
+import org.keycloak.models.mongo.impl.context.TransactionMongoStoreInvocationContext;
+import org.keycloak.models.mongo.utils.SystemPropertiesConfigurationProvider;
 
 import java.net.UnknownHostException;
 import java.util.ArrayList;
@@ -20,22 +25,22 @@ import java.util.List;
  */
 public class MongoDBModelTest {
 
-    private static final Class<? extends NoSQLObject>[] MANAGED_DATA_TYPES = (Class<? extends NoSQLObject>[])new Class<?>[] {
+    private static final Class<? extends MongoEntity>[] MANAGED_DATA_TYPES = (Class<? extends MongoEntity>[])new Class<?>[] {
             Person.class,
             Address.class,
     };
 
     private MongoClient mongoClient;
-    private NoSQL mongoDB;
+    private MongoStore mongoStore;
 
     @Before
     public void before() throws Exception {
         try {
             // TODO: authentication support
-            mongoClient = new MongoClient("localhost", 27017);
+            mongoClient = new MongoClient("localhost", SystemPropertiesConfigurationProvider.getMongoPort());
 
             DB db = mongoClient.getDB("keycloakTest");
-            mongoDB = new MongoDBImpl(db, true, MANAGED_DATA_TYPES);
+            mongoStore = new MongoStoreImpl(db, true, MANAGED_DATA_TYPES);
 
         } catch (UnknownHostException e) {
             throw new RuntimeException(e);
@@ -47,25 +52,27 @@ public class MongoDBModelTest {
         mongoClient.close();
     }
 
-    // @Test
+    @Test
     public void mongoModelTest() throws Exception {
+        MongoStoreInvocationContext context = new TransactionMongoStoreInvocationContext(mongoStore);
+
         // Add some user
         Person john = new Person();
         john.setFirstName("john");
         john.setAge(25);
         john.setGender(Person.Gender.MALE);
 
-        mongoDB.saveObject(john);
+        mongoStore.insertEntity(john, context);
 
         // Add another user
         Person mary = new Person();
         mary.setFirstName("mary");
-        mary.setKids(Arrays.asList(new String[] {"Peter", "Paul", "Wendy"}));
+        mary.setKids(asList("Peter", "Paul", "Wendy"));
 
         Address addr1 = new Address();
         addr1.setStreet("Elm");
         addr1.setNumber(5);
-        addr1.setFlatNumbers(Arrays.asList(new String[] {"flat1", "flat2"}));
+        addr1.setFlatNumbers(asList("flat1", "flat2"));
         Address addr2 = new Address();
         List<Address> addresses = new ArrayList<Address>();
         addresses.add(addr1);
@@ -74,13 +81,14 @@ public class MongoDBModelTest {
         mary.setAddresses(addresses);
         mary.setMainAddress(addr1);
         mary.setGender(Person.Gender.FEMALE);
-        mary.setGenders(Arrays.asList(new Person.Gender[] {Person.Gender.FEMALE}));
-        mongoDB.saveObject(mary);
+        mary.setGenders(asList(Person.Gender.FEMALE));
+
+        mongoStore.insertEntity(mary, context);
 
-        Assert.assertEquals(2, mongoDB.loadObjects(Person.class, mongoDB.createQueryBuilder().build()).size());
+        Assert.assertEquals(2, mongoStore.loadEntities(Person.class, new QueryBuilder().get(), context).size());
 
-        NoSQLQuery query = mongoDB.createQueryBuilder().andCondition("addresses.flatNumbers", "flat1").build();
-        List<Person> persons = mongoDB.loadObjects(Person.class, query);
+        DBObject query = new QueryBuilder().and("addresses.flatNumbers").is("flat1").get();
+        List<Person> persons = mongoStore.loadEntities(Person.class, query, context);
         Assert.assertEquals(1, persons.size());
         mary = persons.get(0);
         Assert.assertEquals(mary.getFirstName(), "mary");
@@ -89,15 +97,15 @@ public class MongoDBModelTest {
         Assert.assertEquals(Address.class, mary.getAddresses().get(0).getClass());
 
         // Test push/pull
-        mongoDB.pushItemToList(mary, "kids", "Pauline");
-        mongoDB.pullItemFromList(mary, "kids", "Paul");
+        mongoStore.pushItemToList(mary, "kids", "Pauline", true, context);
+        mongoStore.pullItemFromList(mary, "kids", "Paul", context);
 
         Address addr3 = new Address();
         addr3.setNumber(6);
         addr3.setStreet("Broadway");
-        mongoDB.pushItemToList(mary, "addresses", addr3);
+        mongoStore.pushItemToList(mary, "addresses", addr3, true, context);
 
-        mary = mongoDB.loadObject(Person.class, mary.getId());
+        mary = mongoStore.loadEntity(Person.class, mary.getId(), context);
         Assert.assertEquals(3, mary.getKids().size());
         Assert.assertTrue(mary.getKids().contains("Pauline"));
         Assert.assertFalse(mary.getKids().contains("Paul"));
@@ -107,5 +115,32 @@ public class MongoDBModelTest {
         Assert.assertEquals(5, mainAddress.getNumber());
         Assert.assertEquals(Person.Gender.FEMALE, mary.getGender());
         Assert.assertTrue(mary.getGenders().contains(Person.Gender.FEMALE));
+
+
+        // Some test of Map (attributes)
+        mary.addAttribute("attr1", "value1");
+        mary.addAttribute("attr2", "value2");
+        mary.addAttribute("attr.some3", "value3");
+        mongoStore.updateEntity(mary, context);
+
+        mary = mongoStore.loadEntity(Person.class, mary.getId(), context);
+        Assert.assertEquals(3, mary.getAttributes().size());
+
+        mary.removeAttribute("attr2");
+        mary.removeAttribute("nonExisting");
+        mongoStore.updateEntity(mary, context);
+
+        mary = mongoStore.loadEntity(Person.class, mary.getId(), context);
+        Assert.assertEquals(2, mary.getAttributes().size());
+        Assert.assertEquals("value1", mary.getAttributes().get("attr1"));
+        Assert.assertEquals("value3", mary.getAttributes().get("attr.some3"));
+
+        context.commit();
+    }
+
+    private <T> List<T> asList(T... objects) {
+        List<T> list = new ArrayList<T>();
+        list.addAll(Arrays.asList(objects));
+        return list;
     }
 }
diff --git a/model/mongo/src/test/java/org/keycloak/models/mongo/test/Person.java b/model/mongo/src/test/java/org/keycloak/models/mongo/test/Person.java
index 7f8d0d9..47ac69b 100755
--- a/model/mongo/src/test/java/org/keycloak/models/mongo/test/Person.java
+++ b/model/mongo/src/test/java/org/keycloak/models/mongo/test/Person.java
@@ -1,19 +1,19 @@
 package org.keycloak.models.mongo.test;
 
-import org.keycloak.models.mongo.api.AbstractNoSQLObject;
-import org.keycloak.models.mongo.api.NoSQLCollection;
-import org.keycloak.models.mongo.api.NoSQLField;
-import org.keycloak.models.mongo.api.NoSQLId;
+import org.keycloak.models.mongo.api.AbstractMongoIdentifiableEntity;
+import org.keycloak.models.mongo.api.MongoCollection;
+import org.keycloak.models.mongo.api.MongoField;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  */
-@NoSQLCollection(collectionName = "persons")
-public class Person extends AbstractNoSQLObject {
+@MongoCollection(collectionName = "persons")
+public class Person extends AbstractMongoIdentifiableEntity {
 
-    private String id;
     private String firstName;
     private int age;
     private List<String> kids;
@@ -21,18 +21,9 @@ public class Person extends AbstractNoSQLObject {
     private Address mainAddress;
     private Gender gender;
     private List<Gender> genders;
+    private Map<String, String> attributes = new HashMap<String, String>();
 
-
-    @NoSQLId
-    public String getId() {
-        return id;
-    }
-
-    public void setId(String id) {
-        this.id = id;
-    }
-
-    @NoSQLField
+    @MongoField
     public String getFirstName() {
         return firstName;
     }
@@ -41,7 +32,7 @@ public class Person extends AbstractNoSQLObject {
         this.firstName = firstName;
     }
 
-    @NoSQLField
+    @MongoField
     public int getAge() {
         return age;
     }
@@ -50,7 +41,7 @@ public class Person extends AbstractNoSQLObject {
         this.age = age;
     }
 
-    @NoSQLField
+    @MongoField
     public Gender getGender() {
         return gender;
     }
@@ -59,7 +50,7 @@ public class Person extends AbstractNoSQLObject {
         this.gender = gender;
     }
 
-    @NoSQLField
+    @MongoField
     public List<Gender> getGenders() {
         return genders;
     }
@@ -68,7 +59,7 @@ public class Person extends AbstractNoSQLObject {
         this.genders = genders;
     }
 
-    @NoSQLField
+    @MongoField
     public List<String> getKids() {
         return kids;
     }
@@ -77,7 +68,7 @@ public class Person extends AbstractNoSQLObject {
         this.kids = kids;
     }
 
-    @NoSQLField
+    @MongoField
     public List<Address> getAddresses() {
         return addresses;
     }
@@ -86,7 +77,7 @@ public class Person extends AbstractNoSQLObject {
         this.addresses = addresses;
     }
 
-    @NoSQLField
+    @MongoField
     public Address getMainAddress() {
         return mainAddress;
     }
@@ -95,6 +86,23 @@ public class Person extends AbstractNoSQLObject {
         this.mainAddress = mainAddress;
     }
 
+    @MongoField
+    public Map<String, String> getAttributes() {
+        return attributes;
+    }
+
+    public void setAttributes(Map<String, String> attributes) {
+        this.attributes = attributes;
+    }
+
+    public void addAttribute(String key, String value) {
+        attributes.put(key, value);
+    }
+
+    public void removeAttribute(String key) {
+        attributes.remove(key);
+    }
+
     public static enum Gender {
         MALE, FEMALE
     }

model/pom.xml 3(+2 -1)

diff --git a/model/pom.xml b/model/pom.xml
index ab454ca..41d5be1 100755
--- a/model/pom.xml
+++ b/model/pom.xml
@@ -37,6 +37,7 @@
         <module>api</module>
         <!-- <module>picketlink</module> -->
         <module>jpa</module>
-        <!-- <module>mongo</module> -->
+        <module>mongo</module>
+        <module>tests</module>
     </modules>
 </project>

model/tests/pom.xml 69(+69 -0)

diff --git a/model/tests/pom.xml b/model/tests/pom.xml
new file mode 100644
index 0000000..c4d7330
--- /dev/null
+++ b/model/tests/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>keycloak-parent</artifactId>
+        <groupId>org.keycloak</groupId>
+        <version>1.0-alpha-2-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>keycloak-model-tests</artifactId>
+    <name>Keycloak Model Tests</name>
+    <description/>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-services</artifactId>
+            <version>${project.version}</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.jackson</groupId>
+            <artifactId>jackson-core-asl</artifactId>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.jackson</groupId>
+            <artifactId>jackson-mapper-asl</artifactId>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>package-tests-jar</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>test-jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>true</skipTests>
+                </configuration>
+            </plugin>
+
+        </plugins>
+    </build>
+
+</project>
diff --git a/model/tests/src/test/java/org/keycloak/model/test/AbstractModelTest.java b/model/tests/src/test/java/org/keycloak/model/test/AbstractModelTest.java
new file mode 100644
index 0000000..1f2fda1
--- /dev/null
+++ b/model/tests/src/test/java/org/keycloak/model/test/AbstractModelTest.java
@@ -0,0 +1,55 @@
+package org.keycloak.model.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.jboss.resteasy.logging.Logger;
+import org.junit.After;
+import org.junit.Before;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.services.resources.KeycloakApplication;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class AbstractModelTest {
+
+    private final Logger log = Logger.getLogger(getClass());
+
+    protected KeycloakSessionFactory factory;
+    protected KeycloakSession identitySession;
+    protected RealmManager realmManager;
+
+    @Before
+    public void before() throws Exception {
+        factory = KeycloakApplication.createSessionFactory();
+        identitySession = factory.createSession();
+        identitySession.getTransaction().begin();
+        realmManager = new RealmManager(identitySession);
+    }
+
+    @After
+    public void after() throws Exception {
+        identitySession.getTransaction().commit();
+        identitySession.close();
+        factory.close();
+    }
+
+    public static RealmRepresentation loadJson(String path) throws IOException {
+        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        int c;
+        while ((c = is.read()) != -1) {
+            os.write(c);
+        }
+        byte[] bytes = os.toByteArray();
+        System.out.println(new String(bytes));
+
+        return JsonSerialization.readValue(bytes, RealmRepresentation.class);
+    }
+}
diff --git a/model/tests/src/test/java/org/keycloak/model/test/CompositeRolesModelTest.java b/model/tests/src/test/java/org/keycloak/model/test/CompositeRolesModelTest.java
new file mode 100644
index 0000000..1baf83e
--- /dev/null
+++ b/model/tests/src/test/java/org/keycloak/model/test/CompositeRolesModelTest.java
@@ -0,0 +1,132 @@
+package org.keycloak.model.test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.services.managers.RealmManager;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class CompositeRolesModelTest extends AbstractModelTest {
+
+    @Before
+    public void before() throws Exception {
+        super.before();
+        RealmManager manager = realmManager;
+        RealmRepresentation rep = AbstractModelTest.loadJson("testcomposites.json");
+        RealmModel realm = manager.createRealm("Test", rep.getRealm());
+        manager.importRealm(rep, realm);
+    }
+
+    @Test
+    public void testAppComposites() {
+        Set<RoleModel> requestedRoles = getRequestedRoles("APP_COMPOSITE_APPLICATION", "APP_COMPOSITE_USER");
+
+        Assert.assertEquals(2, requestedRoles.size());
+        assertContains("APP_ROLE_APPLICATION", "APP_ROLE_1", requestedRoles);
+        assertContains("realm", "REALM_ROLE_1", requestedRoles);
+    }
+
+    @Test
+    public void testRealmAppComposites() {
+        Set<RoleModel> requestedRoles = getRequestedRoles("APP_COMPOSITE_APPLICATION", "REALM_APP_COMPOSITE_USER");
+
+        Assert.assertEquals(1, requestedRoles.size());
+        assertContains("APP_ROLE_APPLICATION", "APP_ROLE_1", requestedRoles);
+    }
+
+    @Test
+    public void testRealmOnlyWithUserCompositeAppComposite() throws Exception {
+        Set<RoleModel> requestedRoles = getRequestedRoles("REALM_COMPOSITE_1_APPLICATION", "REALM_COMPOSITE_1_USER");
+
+        Assert.assertEquals(1, requestedRoles.size());
+        assertContains("realm", "REALM_COMPOSITE_1", requestedRoles);
+    }
+
+    @Test
+    public void testRealmOnlyWithUserCompositeAppRole() throws Exception {
+        Set<RoleModel> requestedRoles = getRequestedRoles("REALM_ROLE_1_APPLICATION", "REALM_COMPOSITE_1_USER");
+
+        Assert.assertEquals(1, requestedRoles.size());
+        assertContains("realm", "REALM_ROLE_1", requestedRoles);
+    }
+
+    @Test
+    public void testRealmOnlyWithUserRoleAppComposite() throws Exception {
+        Set<RoleModel> requestedRoles = getRequestedRoles("REALM_COMPOSITE_1_APPLICATION", "REALM_ROLE_1_USER");
+
+        Assert.assertEquals(1, requestedRoles.size());
+        assertContains("realm", "REALM_ROLE_1", requestedRoles);
+    }
+
+    // TODO: more tests...
+
+    // Same algorithm as in TokenManager.createAccessCode
+    private Set<RoleModel> getRequestedRoles(String applicationName, String username) {
+        Set<RoleModel> requestedRoles = new HashSet<RoleModel>();
+
+        RealmModel realm = realmManager.getRealm("Test");
+        UserModel user = realm.getUser(username);
+        ApplicationModel application = realm.getApplicationByName(applicationName);
+
+        Set<RoleModel> roleMappings = realm.getRoleMappings(user);
+        Set<RoleModel> scopeMappings = realm.getScopeMappings(application.getApplicationUser());
+        Set<RoleModel> appRoles = application.getRoles();
+        if (appRoles != null) scopeMappings.addAll(appRoles);
+
+        for (RoleModel role : roleMappings) {
+            if (role.getContainer().equals(application)) requestedRoles.add(role);
+            for (RoleModel desiredRole : scopeMappings) {
+                Set<RoleModel> visited = new HashSet<RoleModel>();
+                applyScope(role, desiredRole, visited, requestedRoles);
+            }
+        }
+
+        return requestedRoles;
+    }
+
+    private static void applyScope(RoleModel role, RoleModel scope, Set<RoleModel> visited, Set<RoleModel> requested) {
+        if (visited.contains(scope)) return;
+        visited.add(scope);
+        if (role.hasRole(scope)) {
+            requested.add(scope);
+            return;
+        }
+        if (!scope.isComposite()) return;
+
+        for (RoleModel contained : scope.getComposites()) {
+            applyScope(role, contained, visited, requested);
+        }
+    }
+
+    private RoleModel getRole(String appName, String roleName) {
+        RealmModel realm = realmManager.getRealm("Test");
+        if ("realm".equals(appName)) {
+            return realm.getRole(roleName);
+        }  else {
+            return realm.getApplicationByName(appName).getRole(roleName);
+        }
+    }
+
+    private void assertContains(String appName, String roleName, Set<RoleModel> requestedRoles) {
+        RoleModel expectedRole = getRole(appName, roleName);
+
+        Assert.assertTrue(requestedRoles.contains(expectedRole));
+
+        // Check if requestedRole has correct role container
+        for (RoleModel role : requestedRoles) {
+            if (role.equals(expectedRole)) {
+                Assert.assertEquals(role.getContainer(), expectedRole.getContainer());
+            }
+        }
+    }
+}
diff --git a/model/tests/src/test/resources/testcomposites.json b/model/tests/src/test/resources/testcomposites.json
new file mode 100644
index 0000000..73e4300
--- /dev/null
+++ b/model/tests/src/test/resources/testcomposites.json
@@ -0,0 +1,231 @@
+{
+    "id": "Test",
+    "realm": "Test",
+    "enabled": true,
+    "tokenLifespan": 600,
+    "accessCodeLifespan": 600,
+    "accessCodeLifespanUserAction": 600,
+    "sslNotRequired": true,
+    "registrationAllowed": true,
+    "resetPasswordAllowed": true,
+    "requiredCredentials": [ "password" ],
+    "requiredApplicationCredentials": [ "password" ],
+    "requiredOAuthClientCredentials": [ "password" ],
+    "smtpServer": {
+        "from": "auto@keycloak.org",
+        "host": "localhost",
+        "port":"3025"
+    },
+    "users" : [
+        {
+            "username" : "REALM_COMPOSITE_1_USER",
+            "enabled": true,
+            "email" : "test-user@localhost",
+            "credentials" : [
+                { "type" : "password",
+                    "value" : "password" }
+            ]
+        },
+        {
+            "username" : "REALM_ROLE_1_USER",
+            "enabled": true,
+            "email" : "test-user@localhost",
+            "credentials" : [
+                { "type" : "password",
+                    "value" : "password" }
+            ]
+        },
+        {
+            "username" : "REALM_APP_COMPOSITE_USER",
+            "enabled": true,
+            "email" : "test-user@localhost",
+            "credentials" : [
+                { "type" : "password",
+                    "value" : "password" }
+            ]
+        },
+        {
+            "username" : "REALM_APP_ROLE_USER",
+            "enabled": true,
+            "email" : "test-user@localhost",
+            "credentials" : [
+                { "type" : "password",
+                    "value" : "password" }
+            ]
+        },
+        {
+            "username" : "APP_COMPOSITE_USER",
+            "enabled": true,
+            "email" : "test-user@localhost",
+            "credentials" : [
+                { "type" : "password",
+                    "value" : "password" }
+            ]
+        }
+    ],
+    "oauthClients" : [
+        {
+            "name" : "third-party",
+            "enabled": true,
+            "credentials" : [
+                { "type" : "password",
+                    "value" : "password" }
+            ]
+        }
+    ],
+    "roleMappings": [
+        {
+            "username": "REALM_COMPOSITE_1_USER",
+            "roles": ["REALM_COMPOSITE_1"]
+        },
+        {
+            "username": "REALM_ROLE_1_USER",
+            "roles": ["REALM_ROLE_1"]
+        },
+        {
+            "username": "REALM_APP_COMPOSITE_USER",
+            "roles": ["REALM_APP_COMPOSITE_ROLE"]
+        },
+        {
+            "username": "APP_COMPOSITE_USER",
+            "roles": ["REALM_APP_COMPOSITE_ROLE", "REALM_COMPOSITE_1"]
+        }
+    ],
+    "scopeMappings": [
+        {
+            "username": "REALM_COMPOSITE_1_APPLICATION",
+            "roles": ["REALM_COMPOSITE_1"]
+        },
+        {
+            "username": "REALM_ROLE_1_APPLICATION",
+            "roles": ["REALM_ROLE_1"]
+        }
+    ],
+    "applications": [
+        {
+            "name": "REALM_COMPOSITE_1_APPLICATION",
+            "enabled": true,
+            "baseUrl": "http://localhost:8081/app",
+            "adminUrl": "http://localhost:8081/app/logout",
+            "credentials": [
+                {
+                    "type": "password",
+                    "value": "password"
+                }
+            ]
+         },
+        {
+            "name": "REALM_ROLE_1_APPLICATION",
+            "enabled": true,
+            "baseUrl": "http://localhost:8081/app",
+            "adminUrl": "http://localhost:8081/app/logout",
+            "credentials": [
+                {
+                    "type": "password",
+                    "value": "password"
+                }
+            ]
+        },
+        {
+            "name": "APP_ROLE_APPLICATION",
+            "enabled": true,
+            "baseUrl": "http://localhost:8081/app",
+            "adminUrl": "http://localhost:8081/app/logout",
+            "credentials": [
+                {
+                    "type": "password",
+                    "value": "password"
+                }
+            ]
+        },
+        {
+            "name": "APP_COMPOSITE_APPLICATION",
+            "enabled": true,
+            "baseUrl": "http://localhost:8081/app",
+            "adminUrl": "http://localhost:8081/app/logout",
+            "credentials": [
+                {
+                    "type": "password",
+                    "value": "password"
+                }
+            ]
+        }
+    ],
+    "roles" : {
+        "realm" : [
+            {
+                "name": "REALM_ROLE_1"
+            },
+            {
+                "name": "REALM_ROLE_2"
+            },
+            {
+                "name": "REALM_ROLE_3"
+            },
+            {
+                "name": "REALM_COMPOSITE_1",
+                "composites": {
+                    "realm": ["REALM_ROLE_1"]
+                }
+            },
+            {
+                "name": "REALM_APP_COMPOSITE_ROLE",
+                "composites": {
+                    "application": {
+                        "APP_ROLE_APPLICATION" :[
+                            "APP_ROLE_1"
+                        ]
+                    }
+                }
+            }
+        ],
+        "application" : {
+            "APP_ROLE_APPLICATION" : [
+                {
+                    "name": "APP_ROLE_1"
+                },
+                {
+                    "name": "APP_ROLE_2"
+                }
+            ],
+            "APP_COMPOSITE_APPLICATION" : [
+                {
+                    "name": "APP_COMPOSITE_ROLE",
+                    "composites": {
+                        "realm" : [
+                            "REALM_ROLE_1",
+                            "REALM_ROLE_2",
+                            "REALM_ROLE_3"
+                        ],
+                        "application": {
+                            "APP_ROLE_APPLICATION" :[
+                                "APP_ROLE_1"
+                            ]
+                        }
+                    }
+                },
+                {
+                    "name": "APP_ROLE_2"
+                }
+            ]
+        }
+
+    },
+
+    "applicationRoleMappings": {
+        "APP_ROLE_APPLICATION": [
+            {
+                "username": "REALM_APP_ROLE_USER",
+                "roles": ["APP_ROLE_2"]
+            }
+        ]
+    },
+    "applicationScopeMappings": {
+        "APP_ROLE_APPLICATION": [
+            {
+                "username": "APP_COMPOSITE_APPLICATION",
+                "roles": ["APP_ROLE_2"]
+            }
+        ]
+    }
+}
\ No newline at end of file

pom.xml 14(+7 -7)

diff --git a/pom.xml b/pom.xml
index b1aaefd..91b0818 100755
--- a/pom.xml
+++ b/pom.xml
@@ -16,7 +16,7 @@
         <resteasy.version>3.0.6.Final</resteasy.version>
         <undertow.version>1.0.0.Beta30</undertow.version>
         <picketlink.version>2.5.0.Beta6</picketlink.version>
-        <mongo.driver.version>2.11.2</mongo.driver.version>
+        <mongo.driver.version>2.11.3</mongo.driver.version>
         <jboss.logging.version>3.1.1.GA</jboss.logging.version>
         <jboss-logging-tools.version>1.2.0.Beta1</jboss-logging-tools.version>
         <hibernate.javax.persistence.version>1.0.1.Final</hibernate.javax.persistence.version>
@@ -314,11 +314,6 @@
                 <version>2.11.2</version>
             </dependency>
             <dependency>
-                <groupId>de.flapdoodle.embed</groupId>
-                <artifactId>de.flapdoodle.embed.mongo</artifactId>
-                <version>1.27</version>
-            </dependency>
-            <dependency>
                 <groupId>org.apache.jmeter</groupId>
                 <artifactId>ApacheJMeter_java</artifactId>
                 <version>2.9</version>
@@ -395,7 +390,7 @@
                 <plugin>
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-surefire-plugin</artifactId>
-                    <!-- <version>2.5</version> -->
+                    <version>2.16</version>
                     <configuration>
                         <forkMode>once</forkMode>
                         <argLine>-Xms512m -Xmx512m</argLine>
@@ -479,6 +474,11 @@
                     <artifactId>exec-maven-plugin</artifactId>
                     <version>1.2.1</version>
                 </plugin>
+                <plugin>
+                    <groupId>com.github.joelittlejohn.embedmongo</groupId>
+                    <artifactId>embedmongo-maven-plugin</artifactId>
+                    <version>0.1.10</version>
+                </plugin>
             </plugins>
         </pluginManagement>
 

services/pom.xml 51(+0 -51)

diff --git a/services/pom.xml b/services/pom.xml
index d97d6b4..969dc68 100755
--- a/services/pom.xml
+++ b/services/pom.xml
@@ -56,27 +56,6 @@
             <artifactId>keycloak-jaxrs-oauth-client</artifactId>
             <version>${project.version}</version>
         </dependency>
-
-        <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-jpa</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
-        </dependency>
-        <!--
-        <dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-picketlink</artifactId>
-            <version>${project.version}</version>
-            <scope>test</scope>
-        </dependency>
-        -->
-
-        <!--<dependency>
-            <groupId>org.keycloak</groupId>
-            <artifactId>keycloak-model-mongo</artifactId>
-            <version>${project.version}</version>
-        </dependency>-->
         <dependency>
             <groupId>org.keycloak</groupId>
             <artifactId>keycloak-social-core</artifactId>
@@ -128,21 +107,6 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
-            <groupId>org.jboss.resteasy</groupId>
-            <artifactId>resteasy-undertow</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>io.undertow</groupId>
-            <artifactId>undertow-servlet</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>io.undertow</groupId>
-            <artifactId>undertow-core</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>org.codehaus.jackson</groupId>
             <artifactId>jackson-core-asl</artifactId>
             <scope>provided</scope>
@@ -172,21 +136,6 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-            <groupId>org.hibernate.javax.persistence</groupId>
-            <artifactId>hibernate-jpa-2.0-api</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>com.h2database</groupId>
-            <artifactId>h2</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.hibernate</groupId>
-            <artifactId>hibernate-entitymanager</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
             <groupId>com.icegreen</groupId>
             <artifactId>greenmail</artifactId>
             <scope>test</scope>
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 60ee3b8..bc937d7 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -11,6 +11,7 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.RequiredCredentialModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.representations.SkeletonKeyToken;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.services.resources.AccountService;
@@ -41,7 +42,7 @@ public class AuthenticationManager {
 
     public SkeletonKeyToken createIdentityToken(RealmModel realm, String username) {
         SkeletonKeyToken token = new SkeletonKeyToken();
-        token.id(RealmManager.generateId());
+        token.id(KeycloakModelUtils.generateId());
         token.issuedNow();
         token.principal(username);
         token.audience(realm.getName());
diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index 4611599..b9c0e64 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -13,6 +13,7 @@ import org.keycloak.models.SocialLinkModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.UserModel.RequiredAction;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.representations.idm.ApplicationRepresentation;
 import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.representations.idm.OAuthClientRepresentation;
@@ -44,11 +45,6 @@ import java.util.concurrent.atomic.AtomicLong;
  */
 public class RealmManager {
     protected static final Logger logger = Logger.getLogger(RealmManager.class);
-    private static AtomicLong counter = new AtomicLong(1);
-
-    public static String generateId() {
-        return counter.getAndIncrement() + "-" + System.currentTimeMillis();
-    }
 
     protected KeycloakSession identitySession;
 
@@ -73,7 +69,7 @@ public class RealmManager {
     }
 
     public RealmModel createRealm(String id, String name) {
-        if (id == null) id = generateId();
+        if (id == null) id = KeycloakModelUtils.generateId();
         RealmModel realm = identitySession.createRealm(id, name);
         realm.setName(name);
         realm.addRole(Constants.APPLICATION_ROLE);
@@ -166,7 +162,7 @@ public class RealmManager {
     public RealmModel importRealm(RealmRepresentation rep, UserModel realmCreator) {
         String id = rep.getId();
         if (id == null) {
-            id = generateId();
+            id = KeycloakModelUtils.generateId();
         }
         RealmModel realm = createRealm(id, rep.getRealm());
         importRealm(rep, realm);
diff --git a/services/src/main/java/org/keycloak/services/managers/TokenManager.java b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
index 87d4025..a7eeb86 100755
--- a/services/src/main/java/org/keycloak/services/managers/TokenManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/TokenManager.java
@@ -3,9 +3,11 @@ package org.keycloak.services.managers;
 import org.jboss.resteasy.logging.Logger;
 import org.keycloak.jose.jws.JWSBuilder;
 import org.keycloak.models.ApplicationModel;
+import org.keycloak.models.Constants;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.representations.SkeletonKeyScope;
 import org.keycloak.representations.SkeletonKeyToken;
 import org.keycloak.util.Base64Url;
@@ -14,6 +16,7 @@ import org.keycloak.util.JsonSerialization;
 import javax.ws.rs.core.MultivaluedMap;
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -132,7 +135,7 @@ public class TokenManager {
 
     protected SkeletonKeyToken initToken(RealmModel realm, UserModel client, UserModel user) {
         SkeletonKeyToken token = new SkeletonKeyToken();
-        token.id(RealmManager.generateId());
+        token.id(KeycloakModelUtils.generateId());
         token.principal(user.getLoginName());
         token.audience(realm.getName());
         token.issuedNow();
@@ -219,7 +222,7 @@ public class TokenManager {
 
     public SkeletonKeyToken createAccessToken(RealmModel realm, UserModel user) {
         SkeletonKeyToken token = new SkeletonKeyToken();
-        token.id(RealmManager.generateId());
+        token.id(KeycloakModelUtils.generateId());
         token.issuedNow();
         token.principal(user.getLoginName());
         token.audience(realm.getName());
diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
index 6c89c12..83bdaca 100755
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -8,6 +8,7 @@ import org.keycloak.services.managers.ApplianceBootstrap;
 import org.keycloak.services.managers.SocialRequestManager;
 import org.keycloak.services.managers.TokenManager;
 import org.keycloak.services.resources.admin.AdminService;
+import org.keycloak.models.utils.ModelProviderUtils;
 
 import javax.servlet.ServletContext;
 import javax.ws.rs.core.Application;
@@ -15,7 +16,6 @@ import javax.ws.rs.core.Context;
 import javax.ws.rs.core.UriInfo;
 import java.net.URI;
 import java.util.HashSet;
-import java.util.ServiceLoader;
 import java.util.Set;
 
 /**
@@ -26,9 +26,6 @@ public class KeycloakApplication extends Application {
 
     private static final Logger log = Logger.getLogger(KeycloakApplication.class);
 
-    private static final String MODEL_PROVIDER = "keycloak.model";
-    private static final String DEFAULT_MODEL_PROVIDER = "jpa";
-
     protected Set<Object> singletons = new HashSet<Object>();
     protected Set<Class<?>> classes = new HashSet<Class<?>>();
 
@@ -73,28 +70,7 @@ public class KeycloakApplication extends Application {
 
 
     public static KeycloakSessionFactory createSessionFactory() {
-        ServiceLoader<ModelProvider> providers = ServiceLoader.load(ModelProvider.class);
-        String configuredProvider = System.getProperty(MODEL_PROVIDER);
-        ModelProvider provider = null;
-
-        if (configuredProvider != null) {
-            for (ModelProvider p : providers) {
-                if (p.getId().equals(configuredProvider)) {
-                    provider = p;
-                }
-            }
-        } else {
-            for (ModelProvider p : providers) {
-                if (provider == null) {
-                    provider = p;
-                }
-
-                if (p.getId().equals(DEFAULT_MODEL_PROVIDER)) {
-                    provider = p;
-                    break;
-                }
-            }
-        }
+        ModelProvider provider = ModelProviderUtils.getConfiguredModelProvider();
 
         if (provider != null) {
             log.debug("Model provider: " + provider.getId());
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index 115dc5d..d668ee8 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -251,10 +251,22 @@
             <groupId>org.seleniumhq.selenium</groupId>
             <artifactId>selenium-chrome-driver</artifactId>
         </dependency>
+
+        <!-- Mongo dependencies specified here and not in mongo profile, just to allow running tests from IDE -->
+        <dependency>
+            <groupId>org.keycloak</groupId>
+            <artifactId>keycloak-model-mongo</artifactId>
+            <version>${project.version}</version>
+        </dependency>
         <dependency>
             <groupId>org.mongodb</groupId>
             <artifactId>mongo-java-driver</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.picketlink</groupId>
+            <artifactId>picketlink-common</artifactId>
+        </dependency>
+
     </dependencies>
     <build>
         <plugins>
@@ -326,5 +338,84 @@
                 </plugins>
             </build>
         </profile>
+
+        <profile>
+            <id>mongo</id>
+            <activation>
+                <property>
+                    <name>keycloak.model</name>
+                    <value>mongo</value>
+                </property>
+            </activation>
+
+            <properties>
+                <keycloak.mongo.host>localhost</keycloak.mongo.host>
+                <keycloak.mongo.port>27018</keycloak.mongo.port>
+                <keycloak.mongo.db>keycloak</keycloak.mongo.db>
+                <keycloak.mongo.clearOnStartup>true</keycloak.mongo.clearOnStartup>
+            </properties>
+
+            <build>
+                <plugins>
+
+                    <!-- Postpone tests to "integration-test" phase, so that we can bootstrap embedded mongo on 27018 before running tests -->
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>test</id>
+                                <phase>integration-test</phase>
+                                <goals>
+                                    <goal>test</goal>
+                                </goals>
+                                <configuration>
+                                    <systemPropertyVariables>
+                                        <keycloak.mongo.host>${keycloak.mongo.host}</keycloak.mongo.host>
+                                        <keycloak.mongo.port>${keycloak.mongo.port}</keycloak.mongo.port>
+                                        <keycloak.mongo.db>${keycloak.mongo.db}</keycloak.mongo.db>
+                                        <keycloak.mongo.clearOnStartup>${keycloak.mongo.clearOnStartup}</keycloak.mongo.clearOnStartup>
+                                    </systemPropertyVariables>
+                                </configuration>
+                            </execution>
+                            <execution>
+                                <id>default-test</id>
+                                <configuration>
+                                    <skip>true</skip>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+
+                    <!-- Embedded mongo -->
+                    <plugin>
+                        <groupId>com.github.joelittlejohn.embedmongo</groupId>
+                        <artifactId>embedmongo-maven-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>start-mongodb</id>
+                                <phase>pre-integration-test</phase>
+                                <goals>
+                                    <goal>start</goal>
+                                </goals>
+                                <configuration>
+                                    <port>${keycloak.mongo.port}</port>
+                                    <logging>file</logging>
+                                    <logFile>${project.build.directory}/mongodb.log</logFile>
+                                </configuration>
+                            </execution>
+                            <execution>
+                                <id>stop-mongodb</id>
+                                <phase>post-integration-test</phase>
+                                <goals>
+                                    <goal>stop</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+
+        </profile>
     </profiles>
 </project>
\ No newline at end of file
diff --git a/testsuite/integration/README.md b/testsuite/integration/README.md
index 6428657..1b3ed3e 100644
--- a/testsuite/integration/README.md
+++ b/testsuite/integration/README.md
@@ -8,6 +8,13 @@ The testsuite uses Sellenium. By default it uses the HtmlUnit WebDriver, but can
 
 To run the tests with Firefox add `-Dbrowser=firefox` or for Chrome add `-Dbrowser=chrome`
 
+Mongo
+-----
+
+The testsuite is executed with JPA model implementation with data saved in H2 database by default. To run testsuite with Mongo model, just add property `-Dkeycloak.model=mongo` when executing it.
+
+Note that this will automatically run embedded Mongo database on localhost/27018 and it will stop it after whole testsuite is finished.
+So you don't need to have Mongo installed on your laptop to run mongo execution tests.
 
 Test utils
 ==========
@@ -41,6 +48,16 @@ For example to use the example themes run the server with:
     
 **NOTE:** If `keycloak.theme.dir` is specified the default themes (base, rcue and keycloak) are loaded from the classpath
 
+### Run server with Mongo model
+
+To start a Keycloak server with identity model data persisted in Mongo database instead of default JPA/H2 you can run:
+
+    mvn exec:java -Pkeycloak-server -Dkeycloak.model=mongo
+
+By default it's using database `keycloak` on localhost/27017 and it uses already existing data from this DB (no cleanup of existing data during bootstrap). Assumption is that you already have DB running on localhost/27017 . Use system properties to configure things differently:
+
+    mvn exec:java -Pkeycloak-server -Dkeycloak.model=mongo -Dkeycloak.mongo.host=localhost -Dkeycloak.mongo.port=27017 -Dkeycloak.mongo.db=keycloak -Dkeycloak.mongo.clearCollectionsOnStartup=false
+
 TOTP codes
 ----------
 
diff --git a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java
index 136a400..571eda2 100755
--- a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java
+++ b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java
@@ -273,7 +273,7 @@ public class KeycloakServer {
 
         server.deploy(di);
 
-        factory = KeycloakApplication.createSessionFactory();
+        factory = ((KeycloakApplication)deployment.getApplication()).getFactory();
 
         setupDefaultRealm();