keycloak-memoizeit

Mongo: Remove realmAdmins when realm is removed. Refactored

9/10/2013 11:52:13 AM

Changes

services/src/main/java/org/keycloak/services/models/nosql/api/types/ConverterKey.java 31(+0 -31)

services/src/main/java/org/keycloak/services/models/nosql/impl/types/BasicDBListToStringArrayConverter.java 41(+0 -41)

services/src/main/java/org/keycloak/services/models/nosql/impl/Utils.java 75(+0 -75)

Details

diff --git a/services/src/main/java/org/keycloak/services/models/nosql/api/NoSQL.java b/services/src/main/java/org/keycloak/services/models/nosql/api/NoSQL.java
index 1b28fd1..f302567 100644
--- a/services/src/main/java/org/keycloak/services/models/nosql/api/NoSQL.java
+++ b/services/src/main/java/org/keycloak/services/models/nosql/api/NoSQL.java
@@ -4,6 +4,7 @@ import java.util.List;
 
 import org.keycloak.services.models.nosql.api.query.NoSQLQuery;
 import org.keycloak.services.models.nosql.api.query.NoSQLQueryBuilder;
+import org.picketlink.common.properties.Property;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -29,4 +30,8 @@ public interface NoSQL {
     void removeObjects(Class<? extends NoSQLObject> type, NoSQLQuery query);
 
     NoSQLQueryBuilder createQueryBuilder();
+
+    <S> void pushItemToList(NoSQLObject object, String listPropertyName, S itemToPush);
+
+    <S> void pullItemFromList(NoSQLObject object, String listPropertyName, S itemToPull);
 }
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/api/query/NoSQLQueryBuilder.java b/services/src/main/java/org/keycloak/services/models/nosql/api/query/NoSQLQueryBuilder.java
index 341a115..9f2c383 100644
--- a/services/src/main/java/org/keycloak/services/models/nosql/api/query/NoSQLQueryBuilder.java
+++ b/services/src/main/java/org/keycloak/services/models/nosql/api/query/NoSQLQueryBuilder.java
@@ -1,6 +1,7 @@
 package org.keycloak.services.models.nosql.api.query;
 
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -21,7 +22,7 @@ public abstract class NoSQLQueryBuilder {
         return this;
     }
 
-    public abstract NoSQLQueryBuilder inCondition(String name, Object[] values);
+    public abstract NoSQLQueryBuilder inCondition(String name, List<?> values);
 
     protected void put(String name, Object value) {
         queryAttributes.put(name, value);
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/api/types/Converter.java b/services/src/main/java/org/keycloak/services/models/nosql/api/types/Converter.java
index 221db2b..3740201 100644
--- a/services/src/main/java/org/keycloak/services/models/nosql/api/types/Converter.java
+++ b/services/src/main/java/org/keycloak/services/models/nosql/api/types/Converter.java
@@ -8,11 +8,9 @@ package org.keycloak.services.models.nosql.api.types;
  */
 public interface Converter<T, S> {
 
-    T convertDBObjectToApplicationObject(S dbObject);
+    S convertObject(T objectToConvert);
 
-    S convertApplicationObjectToDBObject(T applicationObject);
+    Class<? extends T> getConverterObjectType();
 
-    Class<? extends T> getApplicationObjectType();
-
-    Class<S> getDBObjectType();
+    Class<S> getExpectedReturnType();
 }
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/api/types/TypeConverter.java b/services/src/main/java/org/keycloak/services/models/nosql/api/types/TypeConverter.java
index 3368e4f..fd2020a 100644
--- a/services/src/main/java/org/keycloak/services/models/nosql/api/types/TypeConverter.java
+++ b/services/src/main/java/org/keycloak/services/models/nosql/api/types/TypeConverter.java
@@ -3,6 +3,8 @@ package org.keycloak.services.models.nosql.api.types;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.picketlink.common.reflection.Reflections;
+
 /**
  * Registry of converters, which allow to convert application object to database objects. TypeConverter is main entry point to be used by application.
  * Application can create instance of TypeConverter and then register required Converter objects.
@@ -12,38 +14,101 @@ import java.util.Map;
 public class TypeConverter {
 
     // TODO: Thread-safety support (maybe...)
-    private Map<ConverterKey, Converter<?, ?>> converterRegistry = new HashMap<ConverterKey, Converter<?, ?>>();
+    // Converters of Application objects to DB objects
+    private Map<Class<?>, Converter<?, ?>> appObjectConverters = new HashMap<Class<?>, Converter<?, ?>>();
 
-    public <T, S> void addConverter(Converter<T, S> converter) {
-        ConverterKey converterKey = new ConverterKey(converter.getApplicationObjectType(), converter.getDBObjectType());
-        converterRegistry.put(converterKey, converter);
-    }
+    // Converters of DB objects to Application objects
+    private Map<Class<?>, Map<Class<?>, Converter<?, ?>>> dbObjectConverters = new HashMap<Class<?>, Map<Class<?>, Converter<?,?>>>();
 
-    public <T, S> T convertDBObjectToApplicationObject(S dbObject, Class<T> expectedApplicationObjectType) {
-        // TODO: Not type safe as it expects that S type of converter must exactly match type of dbObject. Converter lookup should be more flexible
-        Class<S> expectedDBObjectType = (Class<S>)dbObject.getClass();
-        Converter<T, S> converter = getConverter(expectedApplicationObjectType, expectedDBObjectType);
-        return converter.convertDBObjectToApplicationObject(dbObject);
+
+    /**
+     * Add converter for converting application objects to DB objects
+     *
+     * @param converter
+     */
+    public void addAppObjectConverter(Converter<?, ?> converter) {
+        appObjectConverters.put(converter.getConverterObjectType(), converter);
     }
 
-    public <T, S> S convertApplicationObjectToDBObject(T applicationObject, Class<S> expectedDBObjectType) {
-        // TODO: Not type safe as it expects that T type of converter must exactly match type of applicationObject. Converter lookup should be more flexible
-        Class<T> expectedApplicationObjectType = (Class<T>)applicationObject.getClass();
-        Converter<T, S> converter = getConverter(expectedApplicationObjectType, expectedDBObjectType);
 
-        return converter.convertApplicationObjectToDBObject(applicationObject);
+    /**
+     * Add converter for converting DB objects to application objects
+     *
+     * @param converter
+     */
+    public void addDBObjectConverter(Converter<?, ?> converter) {
+        Class<?> dbObjectType = converter.getConverterObjectType();
+        Class<?> appObjectType = converter.getExpectedReturnType();
+        Map<Class<?>, Converter<?, ?>> appObjects = dbObjectConverters.get(dbObjectType);
+        if (appObjects == null) {
+            appObjects = new HashMap<Class<?>, Converter<?, ?>>();
+            dbObjectConverters.put(dbObjectType, appObjects);
+        }
+        appObjects.put(appObjectType, converter);
     }
 
-    private <T, S> Converter<T, S> getConverter( Class<T> expectedApplicationObjectType, Class<S> expectedDBObjectType) {
-        ConverterKey key = new ConverterKey(expectedApplicationObjectType, expectedDBObjectType);
-        Converter<T, S> converter = (Converter<T, S>)converterRegistry.get(key);
+
+    public <S> S convertDBObjectToApplicationObject(Object dbObject, Class<S> expectedApplicationObjectType) {
+        Class<?> dbObjectType = dbObject.getClass();
+        Converter<Object, S> converter;
+
+        Map<Class<?>, Converter<?, ?>> appObjects = dbObjectConverters.get(dbObjectType);
+        if (appObjects == null) {
+            throw new IllegalArgumentException("Not found any converters for type " + dbObjectType);
+        } else {
+            if (appObjects.size() == 1) {
+                converter = (Converter<Object, S>)appObjects.values().iterator().next();
+            } else {
+                // Try to find converter for requested application type
+                converter = (Converter<Object, S>)appObjects.get(expectedApplicationObjectType);
+            }
+        }
 
         if (converter == null) {
-            throw new IllegalStateException("Can't found converter for expectedApplicationObject=" + expectedApplicationObjectType + ", expectedDBObjectType=" + expectedDBObjectType);
+            throw new IllegalArgumentException("Can't found converter for type " + dbObjectType + " and expectedApplicationType " + expectedApplicationObjectType);
+        }
+        if (!expectedApplicationObjectType.isAssignableFrom(converter.getExpectedReturnType())) {
+            throw new IllegalArgumentException("Converter " + converter + " has return type " + converter.getExpectedReturnType() +
+                    " but we need type " + expectedApplicationObjectType);
         }
 
-        return converter;
+        return converter.convertObject(dbObject);
     }
 
 
+    public <S> S convertApplicationObjectToDBObject(Object applicationObject, Class<S> expectedDBObjectType) {
+        Class<?> appObjectType = applicationObject.getClass();
+        Converter<Object, S> converter = (Converter<Object, S>)getAppConverterForType(appObjectType);
+        if (converter == null) {
+            throw new IllegalArgumentException("Can't found converter for type " + appObjectType + " in registered appObjectConverters");
+        }
+        if (!expectedDBObjectType.isAssignableFrom(converter.getExpectedReturnType())) {
+            throw new IllegalArgumentException("Converter " + converter + " has return type " + converter.getExpectedReturnType() +
+                    " but we need type " + expectedDBObjectType);
+        }
+        return converter.convertObject(applicationObject);
+    }
+
+    // Try to find converter for given type or all it's supertypes
+    private Converter<Object, ?> getAppConverterForType(Class<?> appObjectType) {
+        Converter<Object, ?> converter = (Converter<Object, ?>)appObjectConverters.get(appObjectType);
+        if (converter != null) {
+            return converter;
+        } else {
+            Class<?>[] interfaces = appObjectType.getInterfaces();
+            for (Class<?> interface1 : interfaces) {
+                converter = getAppConverterForType(interface1);
+                if (converter != null) {
+                    return converter;
+                }
+            }
+
+            Class<?> superType = appObjectType.getSuperclass();
+            if (superType != null) {
+                return getAppConverterForType(superType);
+            } else {
+                return null;
+            }
+        }
+    }
 }
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/impl/MongoDBImpl.java b/services/src/main/java/org/keycloak/services/models/nosql/impl/MongoDBImpl.java
index 0385aee..6862a0c 100644
--- a/services/src/main/java/org/keycloak/services/models/nosql/impl/MongoDBImpl.java
+++ b/services/src/main/java/org/keycloak/services/models/nosql/impl/MongoDBImpl.java
@@ -1,11 +1,13 @@
 package org.keycloak.services.models.nosql.impl;
 
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
+import com.mongodb.BasicDBList;
 import com.mongodb.BasicDBObject;
 import com.mongodb.DB;
 import com.mongodb.DBCollection;
@@ -22,8 +24,11 @@ import org.keycloak.services.models.nosql.api.query.NoSQLQuery;
 import org.keycloak.services.models.nosql.api.query.NoSQLQueryBuilder;
 import org.keycloak.services.models.nosql.api.types.Converter;
 import org.keycloak.services.models.nosql.api.types.TypeConverter;
-import org.keycloak.services.models.nosql.impl.types.BasicDBListToStringArrayConverter;
+import org.keycloak.services.models.nosql.impl.types.ListConverter;
+import org.keycloak.services.models.nosql.impl.types.BasicDBListConverter;
+import org.keycloak.services.models.nosql.impl.types.BasicDBObjectConverter;
 import org.keycloak.services.models.nosql.impl.types.NoSQLObjectConverter;
+import org.keycloak.services.models.nosql.impl.types.SimpleConverter;
 import org.picketlink.common.properties.Property;
 import org.picketlink.common.properties.query.AnnotatedPropertyCriteria;
 import org.picketlink.common.properties.query.PropertyQueries;
@@ -33,6 +38,8 @@ import org.picketlink.common.properties.query.PropertyQueries;
  */
 public class MongoDBImpl implements NoSQL {
 
+    private static final Class<?>[] SIMPLE_TYPES = { String.class, Integer.class, Boolean.class, Long.class, Double.class, Character.class, Date.class };
+
     private final DB database;
     private static final Logger logger = Logger.getLogger(MongoDBImpl.class);
 
@@ -40,14 +47,27 @@ public class MongoDBImpl implements NoSQL {
     private ConcurrentMap<Class<? extends NoSQLObject>, ObjectInfo> objectInfoCache =
             new ConcurrentHashMap<Class<? extends NoSQLObject>, ObjectInfo>();
 
+
     public MongoDBImpl(DB database, boolean removeAllObjectsAtStartup, Class<? extends NoSQLObject>[] managedDataTypes) {
         this.database = database;
 
         typeConverter = new TypeConverter();
-        typeConverter.addConverter(new BasicDBListToStringArrayConverter());
+
+        for (Class<?> simpleConverterClass : SIMPLE_TYPES) {
+            SimpleConverter converter = new SimpleConverter(simpleConverterClass);
+            typeConverter.addAppObjectConverter(converter);
+            typeConverter.addDBObjectConverter(converter);
+        }
+
+        // Specific converter for ArrayList is added just for performance purposes to avoid recursive converter lookup (most of list impl will be ArrayList)
+        typeConverter.addAppObjectConverter(new ListConverter(typeConverter, ArrayList.class));
+        typeConverter.addAppObjectConverter(new ListConverter(typeConverter, List.class));
+        typeConverter.addDBObjectConverter(new BasicDBListConverter(typeConverter));
+
         for (Class<? extends NoSQLObject> type : managedDataTypes) {
-            typeConverter.addConverter(new NoSQLObjectConverter(this, typeConverter, type));
             getObjectInfo(type);
+            typeConverter.addAppObjectConverter(new NoSQLObjectConverter(this, typeConverter, type));
+            typeConverter.addDBObjectConverter(new BasicDBObjectConverter(this, typeConverter, type));
         }
 
         if (removeAllObjectsAtStartup) {
@@ -55,10 +75,10 @@ public class MongoDBImpl implements NoSQL {
                 ObjectInfo objectInfo = getObjectInfo(type);
                 String collectionName = objectInfo.getDbCollectionName();
                 if (collectionName != null) {
-                    logger.debug("Removing all objects of type " + type);
+                    logger.debug("Dropping collection " + collectionName);
 
                     DBCollection dbCollection = this.database.getCollection(collectionName);
-                    dbCollection.remove(new BasicDBObject());
+                    dbCollection.drop();
                 }  else {
                     logger.debug("Skip removing objects of type " + type + " as it doesn't have it's own collection");
                 }
@@ -67,6 +87,7 @@ public class MongoDBImpl implements NoSQL {
         }
     }
 
+
     @Override
     public void saveObject(NoSQLObject object) {
         Class<? extends NoSQLObject> clazz = object.getClass();
@@ -90,12 +111,12 @@ public class MongoDBImpl implements NoSQL {
                 oidProperty.setValue(object, dbObject.getString("_id"));
             }
         } else {
-            BasicDBObject setCommand = new BasicDBObject("$set", dbObject);
             BasicDBObject query = new BasicDBObject("_id", new ObjectId(currentId));
-            dbCollection.update(query, setCommand);
+            dbCollection.update(query, dbObject);
         }
     }
 
+
     @Override
     public <T extends NoSQLObject> T loadObject(Class<T> type, String oid) {
         DBCollection dbCollection = getDBCollectionForType(type);
@@ -106,6 +127,7 @@ public class MongoDBImpl implements NoSQL {
         return typeConverter.convertDBObjectToApplicationObject(dbObject, type);
     }
 
+
     @Override
     public <T extends NoSQLObject> T loadSingleObject(Class<T> type, NoSQLQuery query) {
         List<T> result = loadObjects(type, query);
@@ -119,6 +141,7 @@ public class MongoDBImpl implements NoSQL {
         }
     }
 
+
     @Override
     public <T extends NoSQLObject> List<T> loadObjects(Class<T> type, NoSQLQuery query) {
         DBCollection dbCollection = getDBCollectionForType(type);
@@ -129,6 +152,7 @@ public class MongoDBImpl implements NoSQL {
         return convertCursor(type, cursor);
     }
 
+
     @Override
     public void removeObject(NoSQLObject object) {
         Class<? extends NoSQLObject> type = object.getClass();
@@ -140,6 +164,7 @@ public class MongoDBImpl implements NoSQL {
         removeObject(type, oid);
     }
 
+
     @Override
     public void removeObject(Class<? extends NoSQLObject> type, String oid) {
         NoSQLObject found = loadObject(type, oid);
@@ -155,6 +180,7 @@ public class MongoDBImpl implements NoSQL {
         }
     }
 
+
     @Override
     public void removeObjects(Class<? extends NoSQLObject> type, NoSQLQuery query) {
         List<? extends NoSQLObject> foundObjects = loadObjects(type, query);
@@ -172,14 +198,81 @@ public class MongoDBImpl implements NoSQL {
         }
     }
 
+
     @Override
     public NoSQLQueryBuilder createQueryBuilder() {
         return new MongoDBQueryBuilder();
     }
 
+
+    @Override
+    public <S> void pushItemToList(NoSQLObject object, String listPropertyName, S itemToPush) {
+        Class<? extends NoSQLObject> type = object.getClass();
+        ObjectInfo objectInfo = getObjectInfo(type);
+
+        Property<String> oidProperty = getObjectInfo(type).getOidProperty();
+        if (oidProperty == null) {
+            throw new IllegalArgumentException("List pushes not supported for properties without oid");
+        }
+
+        // Add item to list directly in this object
+        Property<Object> listProperty = objectInfo.getPropertyByName(listPropertyName);
+        if (listProperty == null) {
+            throw new IllegalArgumentException("Property " + listPropertyName + " doesn't exist on object " + object);
+        }
+
+        List<S> list = (List<S>)listProperty.getValue(object);
+        if (list == null) {
+            list = new ArrayList<S>();
+            listProperty.setValue(object, list);
+        }
+        list.add(itemToPush);
+
+        // Push item to DB. We always convert whole list, so it's not so optimal...
+        BasicDBList dbList = typeConverter.convertApplicationObjectToDBObject(list, BasicDBList.class);
+
+        BasicDBObject query = new BasicDBObject("_id", new ObjectId(oidProperty.getValue(object)));
+        BasicDBObject listObject = new BasicDBObject(listPropertyName, dbList);
+        BasicDBObject setCommand = new BasicDBObject("$set", listObject);
+        getDBCollectionForType(type).update(query, setCommand);
+    }
+
+
+    @Override
+    public <S> void pullItemFromList(NoSQLObject object, String listPropertyName, S itemToPull) {
+        Class<? extends NoSQLObject> type = object.getClass();
+        ObjectInfo objectInfo = getObjectInfo(type);
+
+        Property<String> oidProperty = getObjectInfo(type).getOidProperty();
+        if (oidProperty == null) {
+            throw new IllegalArgumentException("List pulls not supported for properties without oid");
+        }
+
+        // Remove item from list directly in this object
+        Property<Object> listProperty = objectInfo.getPropertyByName(listPropertyName);
+        if (listProperty == null) {
+            throw new IllegalArgumentException("Property " + listPropertyName + " doesn't exist on object " + object);
+        }
+        List<S> list = (List<S>)listProperty.getValue(object);
+        if (list != null) {
+            list.remove(itemToPull);
+        }
+
+        // Pull item from DB
+        Object dbItemToPull = typeConverter.convertApplicationObjectToDBObject(itemToPull, Object.class);
+        BasicDBObject query = new BasicDBObject("_id", new ObjectId(oidProperty.getValue(object)));
+        BasicDBObject pullObject = new BasicDBObject(listPropertyName, dbItemToPull);
+        BasicDBObject pullCommand = new BasicDBObject("$pull", pullObject);
+        getDBCollectionForType(type).update(query, pullCommand);
+    }
+
     // Possibility to add user-defined converters
-    public void addConverter(Converter<?, ?> converter) {
-        typeConverter.addConverter(converter);
+    public void addAppObjectConverter(Converter<?, ?> converter) {
+        typeConverter.addAppObjectConverter(converter);
+    }
+
+    public void addDBObjectConverter(Converter<?, ?> converter) {
+        typeConverter.addDBObjectConverter(converter);
     }
 
     public ObjectInfo getObjectInfo(Class<? extends NoSQLObject> objectClass) {
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/impl/MongoDBQueryBuilder.java b/services/src/main/java/org/keycloak/services/models/nosql/impl/MongoDBQueryBuilder.java
index 94b6196..80f5efb 100644
--- a/services/src/main/java/org/keycloak/services/models/nosql/impl/MongoDBQueryBuilder.java
+++ b/services/src/main/java/org/keycloak/services/models/nosql/impl/MongoDBQueryBuilder.java
@@ -1,5 +1,9 @@
 package org.keycloak.services.models.nosql.impl;
 
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
 import com.mongodb.BasicDBObject;
 import org.bson.types.ObjectId;
 import org.keycloak.services.models.nosql.api.query.NoSQLQueryBuilder;
@@ -12,18 +16,17 @@ public class MongoDBQueryBuilder extends NoSQLQueryBuilder {
     protected MongoDBQueryBuilder() {};
 
     @Override
-    public NoSQLQueryBuilder inCondition(String name, Object[] values) {
+    public NoSQLQueryBuilder inCondition(String name, List<?> values) {
         if (values == null) {
-            values = new Object[0];
+            values = new LinkedList<Object>();
         }
 
         if ("_id".equals(name)) {
             // we need to convert Strings to ObjectID
-            ObjectId[] objIds = new ObjectId[values.length];
-            for (int i=0 ; i<values.length ; i++) {
-                String id = values[i].toString();
-                ObjectId objectId = new ObjectId(id);
-                objIds[i] = objectId;
+            List<ObjectId> objIds = new ArrayList<ObjectId>();
+            for (Object object : values) {
+                ObjectId objectId = new ObjectId(object.toString());
+                objIds.add(objectId);
             }
             values = objIds;
         }
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/impl/ObjectInfo.java b/services/src/main/java/org/keycloak/services/models/nosql/impl/ObjectInfo.java
index 27f782c..da9d279 100644
--- a/services/src/main/java/org/keycloak/services/models/nosql/impl/ObjectInfo.java
+++ b/services/src/main/java/org/keycloak/services/models/nosql/impl/ObjectInfo.java
@@ -1,6 +1,10 @@
 package org.keycloak.services.models.nosql.impl;
 
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.keycloak.services.models.nosql.api.NoSQLObject;
 import org.picketlink.common.properties.Property;
@@ -16,13 +20,18 @@ public class ObjectInfo {
 
     private final Property<String> oidProperty;
 
-    private final List<Property<Object>> properties;
+    private final Map<String, Property<Object>> properties;
 
     public ObjectInfo(Class<? extends NoSQLObject> objectClass, String dbCollectionName, Property<String> oidProperty, List<Property<Object>> properties) {
         this.objectClass = objectClass;
         this.dbCollectionName = dbCollectionName;
         this.oidProperty = oidProperty;
-        this.properties = properties;
+
+        Map<String, Property<Object>> props= new HashMap<String, Property<Object>>();
+        for (Property<Object> property : properties) {
+            props.put(property.getName(), property);
+        }
+        this.properties = Collections.unmodifiableMap(props);
     }
 
     public Class<? extends NoSQLObject> getObjectClass() {
@@ -37,17 +46,11 @@ public class ObjectInfo {
         return oidProperty;
     }
 
-    public List<Property<Object>> getProperties() {
-        return properties;
+    public Collection<Property<Object>> getProperties() {
+        return properties.values();
     }
 
     public Property<Object> getPropertyByName(String propertyName) {
-        for (Property<Object> property : properties) {
-            if (propertyName.equals(property.getName())) {
-                return property;
-            }
-        }
-
-        return null;
+        return properties.get(propertyName);
     }
 }
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/impl/types/BasicDBListConverter.java b/services/src/main/java/org/keycloak/services/models/nosql/impl/types/BasicDBListConverter.java
new file mode 100644
index 0000000..87941d3
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/models/nosql/impl/types/BasicDBListConverter.java
@@ -0,0 +1,64 @@
+package org.keycloak.services.models.nosql.impl.types;
+
+import java.util.ArrayList;
+
+import com.mongodb.BasicDBList;
+import com.mongodb.BasicDBObject;
+import org.keycloak.services.models.nosql.api.types.Converter;
+import org.keycloak.services.models.nosql.api.types.TypeConverter;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class BasicDBListConverter implements Converter<BasicDBList, ArrayList> {
+
+    private final TypeConverter typeConverter;
+
+    public BasicDBListConverter(TypeConverter typeConverter) {
+        this.typeConverter = typeConverter;
+    }
+
+    @Override
+    public ArrayList convertObject(BasicDBList dbList) {
+        ArrayList<Object> appObjects = new ArrayList<Object>();
+        Class<?> expectedListElementType = null;
+        for (Object dbObject : dbList) {
+
+            if (expectedListElementType == null) {
+                expectedListElementType = findExpectedListElementType(dbObject);
+            }
+
+            appObjects.add(typeConverter.convertDBObjectToApplicationObject(dbObject, expectedListElementType));
+        }
+        return appObjects;
+    }
+
+    @Override
+    public Class<? extends BasicDBList> getConverterObjectType() {
+        return BasicDBList.class;
+    }
+
+    @Override
+    public Class<ArrayList> getExpectedReturnType() {
+        return ArrayList.class;
+    }
+
+    private Class<?> findExpectedListElementType(Object dbObject) {
+        if (dbObject instanceof BasicDBObject) {
+            BasicDBObject basicDBObject = (BasicDBObject) dbObject;
+            String type = (String)basicDBObject.get(ListConverter.OBJECT_TYPE);
+            if (type == null) {
+                throw new IllegalStateException("Not found OBJECT_TYPE key inside object " + dbObject);
+            }
+            basicDBObject.remove(ListConverter.OBJECT_TYPE);
+
+            try {
+                return Class.forName(type);
+            } catch (ClassNotFoundException cnfe) {
+                throw new RuntimeException(cnfe);
+            }
+        } else {
+            return Object.class;
+        }
+    }
+}
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/impl/types/BasicDBObjectConverter.java b/services/src/main/java/org/keycloak/services/models/nosql/impl/types/BasicDBObjectConverter.java
new file mode 100644
index 0000000..4257fb4
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/models/nosql/impl/types/BasicDBObjectConverter.java
@@ -0,0 +1,102 @@
+package org.keycloak.services.models.nosql.impl.types;
+
+import com.mongodb.BasicDBObject;
+import org.jboss.resteasy.logging.Logger;
+import org.keycloak.services.models.nosql.api.AttributedNoSQLObject;
+import org.keycloak.services.models.nosql.api.NoSQLObject;
+import org.keycloak.services.models.nosql.api.types.Converter;
+import org.keycloak.services.models.nosql.api.types.TypeConverter;
+import org.keycloak.services.models.nosql.impl.MongoDBImpl;
+import org.keycloak.services.models.nosql.impl.ObjectInfo;
+import org.picketlink.common.properties.Property;
+import org.picketlink.common.reflection.Types;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class BasicDBObjectConverter<S extends NoSQLObject> implements Converter<BasicDBObject, S> {
+
+    private static final Logger logger = Logger.getLogger(BasicDBObjectConverter.class);
+
+    private final MongoDBImpl mongoDBImpl;
+    private final TypeConverter typeConverter;
+    private final Class<S> expectedNoSQLObjectType;
+
+    public BasicDBObjectConverter(MongoDBImpl mongoDBImpl, TypeConverter typeConverter, Class<S> expectedNoSQLObjectType) {
+        this.mongoDBImpl = mongoDBImpl;
+        this.typeConverter = typeConverter;
+        this.expectedNoSQLObjectType = expectedNoSQLObjectType;
+    }
+
+    @Override
+    public S convertObject(BasicDBObject dbObject) {
+        if (dbObject == null) {
+            return null;
+        }
+
+        ObjectInfo objectInfo = mongoDBImpl.getObjectInfo(expectedNoSQLObjectType);
+
+        S object;
+        try {
+            object = expectedNoSQLObjectType.newInstance();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+
+        for (String key : dbObject.keySet()) {
+            Object value = dbObject.get(key);
+            Property<Object> property;
+
+            if ("_id".equals(key)) {
+                // Current property is "id"
+                Property<String> idProperty = objectInfo.getOidProperty();
+                if (idProperty != null) {
+                    idProperty.setValue(object, value.toString());
+                }
+
+            } else if ((property = objectInfo.getPropertyByName(key)) != null) {
+                // It's declared property with @DBField annotation
+                setPropertyValue(object, value, property);
+
+            } else if (object instanceof AttributedNoSQLObject) {
+                // It's attributed object and property is not declared, so we will call setAttribute
+                ((AttributedNoSQLObject)object).setAttribute(key, value.toString());
+
+            } else {
+                // Show warning if it's unknown
+                logger.warn("Property with key " + key + " not known for type " + expectedNoSQLObjectType);
+            }
+        }
+
+        return object;
+    }
+
+    private void setPropertyValue(NoSQLObject object, Object valueFromDB, Property property) {
+        if (valueFromDB == null) {
+            property.setValue(object, null);
+            return;
+        }
+
+        Class<?> expectedReturnType = property.getJavaClass();
+        // handle primitives
+        expectedReturnType = Types.boxedClass(expectedReturnType);
+
+        Object appObject = typeConverter.convertDBObjectToApplicationObject(valueFromDB, expectedReturnType);
+        if (Types.boxedClass(property.getJavaClass()).isAssignableFrom(appObject.getClass())) {
+            property.setValue(object, appObject);
+        } else {
+            throw new IllegalStateException("Converted object " + appObject + " is not of type " +  expectedReturnType +
+                    ". So can't be assigned as property " + property.getName() + " of " + object.getClass());
+        }
+    }
+
+    @Override
+    public Class<? extends BasicDBObject> getConverterObjectType() {
+        return BasicDBObject.class;
+    }
+
+    @Override
+    public Class<S> getExpectedReturnType() {
+        return expectedNoSQLObjectType;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/impl/types/ListConverter.java b/services/src/main/java/org/keycloak/services/models/nosql/impl/types/ListConverter.java
new file mode 100644
index 0000000..a9685ab
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/models/nosql/impl/types/ListConverter.java
@@ -0,0 +1,52 @@
+package org.keycloak.services.models.nosql.impl.types;
+
+import java.util.List;
+
+import com.mongodb.BasicDBList;
+import com.mongodb.BasicDBObject;
+import org.keycloak.services.models.nosql.api.types.Converter;
+import org.keycloak.services.models.nosql.api.types.TypeConverter;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ListConverter<T extends List> implements Converter<T, BasicDBList> {
+
+    // Key for ObjectType field, which points to actual Java type of element objects inside list
+    static final String OBJECT_TYPE = "OBJECT_TYPE";
+
+    private final TypeConverter typeConverter;
+    private final Class<T> listType;
+
+    public ListConverter(TypeConverter typeConverter, Class<T> listType) {
+        this.typeConverter = typeConverter;
+        this.listType = listType;
+    }
+
+    @Override
+    public BasicDBList convertObject(T appObjectsList) {
+        BasicDBList dbObjects = new BasicDBList();
+        for (Object appObject : appObjectsList) {
+            Object dbObject = typeConverter.convertApplicationObjectToDBObject(appObject, Object.class);
+
+            // We need to add OBJECT_TYPE key to object, so we can retrieve correct Java type of object during load of this list
+            if (dbObject instanceof BasicDBObject) {
+                BasicDBObject basicDBObject = (BasicDBObject)dbObject;
+                basicDBObject.put(OBJECT_TYPE, appObject.getClass().getName());
+            }
+
+            dbObjects.add(dbObject);
+        }
+        return dbObjects;
+    }
+
+    @Override
+    public Class<? extends T> getConverterObjectType() {
+        return listType;
+    }
+
+    @Override
+    public Class<BasicDBList> getExpectedReturnType() {
+        return BasicDBList.class;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/impl/types/NoSQLObjectConverter.java b/services/src/main/java/org/keycloak/services/models/nosql/impl/types/NoSQLObjectConverter.java
index a3974b3..c244607 100644
--- a/services/src/main/java/org/keycloak/services/models/nosql/impl/types/NoSQLObjectConverter.java
+++ b/services/src/main/java/org/keycloak/services/models/nosql/impl/types/NoSQLObjectConverter.java
@@ -1,5 +1,6 @@
 package org.keycloak.services.models.nosql.impl.types;
 
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 
@@ -29,84 +30,18 @@ public class NoSQLObjectConverter<T extends NoSQLObject> implements Converter<T,
     }
 
     @Override
-    public T convertDBObjectToApplicationObject(BasicDBObject dbObject) {
-        if (dbObject == null) {
-            return null;
-        }
-
-        ObjectInfo objectInfo = mongoDBImpl.getObjectInfo(expectedNoSQLObjectType);
-
-        T object;
-        try {
-            object = expectedNoSQLObjectType.newInstance();
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
-
-        for (String key : dbObject.keySet()) {
-            Object value = dbObject.get(key);
-            Property<Object> property;
-
-            if ("_id".equals(key)) {
-                // Current property is "id"
-                Property<String> idProperty = objectInfo.getOidProperty();
-                if (idProperty != null) {
-                    idProperty.setValue(object, value.toString());
-                }
-
-            } else if ((property = objectInfo.getPropertyByName(key)) != null) {
-                // It's declared property with @DBField annotation
-                setPropertyValue(object, value, property);
-
-            } else if (object instanceof AttributedNoSQLObject) {
-                // It's attributed object and property is not declared, so we will call setAttribute
-                ((AttributedNoSQLObject)object).setAttribute(key, value.toString());
-
-            } else {
-                // Show warning if it's unknown
-                // TODO: logging
-                // logger.warn("Property with key " + key + " not known for type " + type);
-                System.err.println("Property with key " + key + " not known for type " + expectedNoSQLObjectType);
-            }
-        }
-
-        return object;
-    }
-
-    private void setPropertyValue(NoSQLObject object, Object valueFromDB, Property property) {
-        Class<?> expectedType = property.getJavaClass();
-        Class actualType = valueFromDB != null ? valueFromDB.getClass() : expectedType;
-
-        // handle primitives
-        expectedType = Types.boxedClass(expectedType);
-        actualType = Types.boxedClass(actualType);
-
-        if (actualType.isAssignableFrom(expectedType)) {
-            property.setValue(object, valueFromDB);
-        } else {
-            // we need to convert
-            Object convertedValue = typeConverter.convertDBObjectToApplicationObject(valueFromDB, expectedType);
-            property.setValue(object, convertedValue);
-        }
-    }
-
-    @Override
-    public BasicDBObject convertApplicationObjectToDBObject(T applicationObject) {
+    public BasicDBObject convertObject(T applicationObject) {
         ObjectInfo objectInfo = mongoDBImpl.getObjectInfo(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();
-        List<Property<Object>> props = objectInfo.getProperties();
+        Collection<Property<Object>> props = objectInfo.getProperties();
         for (Property<Object> property : props) {
             String propName = property.getName();
             Object propValue = property.getValue(applicationObject);
 
-            // Check if we have noSQLObject, which is indication that we need to convert recursively
-            if (propValue instanceof NoSQLObject) {
-                propValue = typeConverter.convertApplicationObjectToDBObject(propValue, BasicDBObject.class);
-            }
-
-            dbObject.append(propName, propValue);
+            Object dbValue = propValue == null ? null : typeConverter.convertApplicationObjectToDBObject(propValue, Types.boxedClass(property.getJavaClass()));
+            dbObject.put(propName, dbValue);
         }
 
         // Adding attributes
@@ -122,12 +57,12 @@ public class NoSQLObjectConverter<T extends NoSQLObject> implements Converter<T,
     }
 
     @Override
-    public Class<T> getApplicationObjectType() {
+    public Class<? extends T> getConverterObjectType() {
         return expectedNoSQLObjectType;
     }
 
     @Override
-    public Class<BasicDBObject> getDBObjectType() {
+    public Class<BasicDBObject> getExpectedReturnType() {
         return BasicDBObject.class;
     }
 }
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/impl/types/SimpleConverter.java b/services/src/main/java/org/keycloak/services/models/nosql/impl/types/SimpleConverter.java
new file mode 100644
index 0000000..8dc1b62
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/models/nosql/impl/types/SimpleConverter.java
@@ -0,0 +1,30 @@
+package org.keycloak.services.models.nosql.impl.types;
+
+import org.keycloak.services.models.nosql.api.types.Converter;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class SimpleConverter<T> implements Converter<T, T> {
+
+    private final Class<T> expectedType;
+
+    public SimpleConverter(Class<T> expectedType) {
+        this.expectedType = expectedType;
+    }
+
+    @Override
+    public T convertObject(T objectToConvert) {
+        return objectToConvert;
+    }
+
+    @Override
+    public Class<? extends T> getConverterObjectType() {
+        return expectedType;
+    }
+
+    @Override
+    public Class<T> getExpectedReturnType() {
+        return expectedType;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/adapters/ApplicationAdapter.java b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/adapters/ApplicationAdapter.java
index dd10400..4d7a564 100644
--- a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/adapters/ApplicationAdapter.java
+++ b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/adapters/ApplicationAdapter.java
@@ -10,9 +10,6 @@ import org.keycloak.services.models.RoleModel;
 import org.keycloak.services.models.UserModel;
 import org.keycloak.services.models.nosql.api.NoSQL;
 import org.keycloak.services.models.nosql.api.query.NoSQLQuery;
-import org.keycloak.services.models.nosql.api.query.NoSQLQueryBuilder;
-import org.keycloak.services.models.nosql.impl.MongoDBQueryBuilder;
-import org.keycloak.services.models.nosql.impl.Utils;
 import org.keycloak.services.models.nosql.keycloak.data.ApplicationData;
 import org.keycloak.services.models.nosql.keycloak.data.RoleData;
 import org.keycloak.services.models.nosql.keycloak.data.UserData;
@@ -138,7 +135,7 @@ public class ApplicationAdapter implements ApplicationModel {
     @Override
     public Set<String> getRoleMappings(UserModel user) {
         UserData userData = ((UserAdapter)user).getUser();
-        String[] roleIds = userData.getRoleIds();
+        List<String> roleIds = userData.getRoleIds();
 
         Set<String> result = new HashSet<String>();
 
@@ -146,7 +143,7 @@ public class ApplicationAdapter implements ApplicationModel {
                 .inCondition("_id", roleIds)
                 .build();
         List<RoleData> roles = noSQL.loadObjects(RoleData.class, query);
-        // TODO: Maybe improve to have roles and scopes in separate table? As actually we need to obtain all roles and then filter programmatically...
+        // 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());
@@ -168,19 +165,13 @@ public class ApplicationAdapter implements ApplicationModel {
     @Override
     public void addScope(UserModel agent, RoleModel role) {
         UserData userData = ((UserAdapter)agent).getUser();
-        RoleData roleData = ((RoleAdapter)role).getRole();
-
-        String[] scopeIds = userData.getScopeIds();
-        scopeIds = Utils.addItemToArray(scopeIds, roleData.getId());
-        userData.setScopeIds(scopeIds);
-
-        noSQL.saveObject(userData);
+        noSQL.pushItemToList(userData, "scopeIds", role.getId());
     }
 
     @Override
     public Set<String> getScope(UserModel agent) {
         UserData userData = ((UserAdapter)agent).getUser();
-        String[] scopeIds = userData.getScopeIds();
+        List<String> scopeIds = userData.getScopeIds();
 
         Set<String> result = new HashSet<String>();
 
@@ -188,7 +179,7 @@ public class ApplicationAdapter implements ApplicationModel {
                 .inCondition("_id", scopeIds)
                 .build();
         List<RoleData> roles = noSQL.loadObjects(RoleData.class, query);
-        // TODO: Maybe improve to have roles and scopes in separate table? As actually we need to obtain all roles and then filter programmatically...
+        // 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());
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/adapters/RealmAdapter.java b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/adapters/RealmAdapter.java
index f7e3e83..2165165 100644
--- a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/adapters/RealmAdapter.java
+++ b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/adapters/RealmAdapter.java
@@ -23,7 +23,6 @@ import org.keycloak.services.models.UserCredentialModel;
 import org.keycloak.services.models.UserModel;
 import org.keycloak.services.models.nosql.api.NoSQL;
 import org.keycloak.services.models.nosql.api.query.NoSQLQuery;
-import org.keycloak.services.models.nosql.api.query.NoSQLQueryBuilder;
 import org.keycloak.services.models.nosql.keycloak.credentials.PasswordCredentialHandler;
 import org.keycloak.services.models.nosql.keycloak.credentials.TOTPCredentialHandler;
 import org.keycloak.services.models.nosql.keycloak.data.ApplicationData;
@@ -32,11 +31,7 @@ import org.keycloak.services.models.nosql.keycloak.data.RequiredCredentialData;
 import org.keycloak.services.models.nosql.keycloak.data.RoleData;
 import org.keycloak.services.models.nosql.keycloak.data.SocialLinkData;
 import org.keycloak.services.models.nosql.keycloak.data.UserData;
-import org.keycloak.services.models.nosql.impl.MongoDBQueryBuilder;
-import org.keycloak.services.models.nosql.impl.Utils;
-import org.keycloak.services.models.picketlink.relationships.ResourceRelationship;
 import org.picketlink.idm.credential.Credentials;
-import org.picketlink.idm.query.RelationshipQuery;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -323,7 +318,7 @@ public class RealmAdapter implements RealmModel {
 
     @Override
     public List<RoleModel> getDefaultRoles() {
-        String[] defaultRoles = realm.getDefaultRoles();
+        List<String> defaultRoles = realm.getDefaultRoles();
 
         NoSQLQuery query = noSQL.createQueryBuilder()
                 .inCondition("_id", defaultRoles)
@@ -344,25 +339,20 @@ public class RealmAdapter implements RealmModel {
             role = addRole(name);
         }
 
-        String[] defaultRoles = realm.getDefaultRoles();
-        String[] roleIds = Utils.addItemToArray(defaultRoles, role.getId());
-
-        realm.setDefaultRoles(roleIds);
-        updateRealm();
+        noSQL.pushItemToList(realm, "defaultRoles", role.getId());
     }
 
     @Override
     public void updateDefaultRoles(String[] defaultRoles) {
         // defaultRoles is array with names of roles. So we need to convert to array of ids
-        String[] roleIds = new String[defaultRoles.length];
-        for (int i=0 ; i<defaultRoles.length ; i++) {
-            String roleName = defaultRoles[i];
+        List<String> roleIds = new ArrayList<String>();
+        for (String roleName : defaultRoles) {
             RoleModel role = getRole(roleName);
             if (role == null) {
                 role = addRole(roleName);
             }
 
-            roleIds[i] = role.getId();
+            roleIds.add(role.getId());
         }
 
         realm.setDefaultRoles(roleIds);
@@ -425,7 +415,7 @@ public class RealmAdapter implements RealmModel {
     public boolean hasRole(UserModel user, RoleModel role) {
         UserData userData = ((UserAdapter)user).getUser();
 
-        String[] roleIds = userData.getRoleIds();
+        List<String> roleIds = userData.getRoleIds();
         String roleId = role.getId();
         if (roleIds != null) {
             for (String currentId : roleIds) {
@@ -440,19 +430,13 @@ public class RealmAdapter implements RealmModel {
     @Override
     public void grantRole(UserModel user, RoleModel role) {
         UserData userData = ((UserAdapter)user).getUser();
-        RoleData roleData = ((RoleAdapter)role).getRole();
-
-        String[] roleIds = userData.getRoleIds();
-        roleIds = Utils.addItemToArray(roleIds, roleData.getId());
-        userData.setRoleIds(roleIds);
-
-        noSQL.saveObject(userData);
+        noSQL.pushItemToList(userData, "roleIds", role.getId());
     }
 
     @Override
     public Set<String> getRoleMappings(UserModel user) {
         UserData userData = ((UserAdapter)user).getUser();
-        String[] roleIds = userData.getRoleIds();
+        List<String> roleIds = userData.getRoleIds();
 
         Set<String> result = new HashSet<String>();
 
@@ -460,7 +444,7 @@ public class RealmAdapter implements RealmModel {
                 .inCondition("_id", roleIds)
                 .build();
         List<RoleData> roles = noSQL.loadObjects(RoleData.class, query);
-        // TODO: Maybe improve to have roles and scopes in separate table? As actually we need to obtain all roles and then filter programmatically...
+        // 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());
@@ -476,19 +460,14 @@ public class RealmAdapter implements RealmModel {
         if (role == null) {
             throw new RuntimeException("Role not found");
         }
-        RoleData roleData = role.getRole();
-
-        String[] scopeIds = userData.getScopeIds();
-        scopeIds = Utils.addItemToArray(scopeIds, roleData.getId());
-        userData.setScopeIds(scopeIds);
 
-        noSQL.saveObject(userData);
+        noSQL.pushItemToList(userData, "scopeIds", role.getId());
     }
 
     @Override
     public Set<String> getScope(UserModel agent) {
         UserData userData = ((UserAdapter)agent).getUser();
-        String[] scopeIds = userData.getScopeIds();
+        List<String> scopeIds = userData.getScopeIds();
 
         Set<String> result = new HashSet<String>();
 
@@ -496,7 +475,7 @@ public class RealmAdapter implements RealmModel {
                 .inCondition("_id", scopeIds)
                 .build();
         List<RoleData> roles = noSQL.loadObjects(RoleData.class, query);
-        // TODO: Maybe improve to have roles and scopes in separate table? As actually we need to obtain all roles and then filter programmatically...
+        // 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());
@@ -507,20 +486,16 @@ public class RealmAdapter implements RealmModel {
 
     @Override
     public boolean isRealmAdmin(UserModel agent) {
-        String[] realmAdmins = realm.getRealmAdmins();
+        List<String> realmAdmins = realm.getRealmAdmins();
         String userId = ((UserAdapter)agent).getUser().getId();
-        return Utils.contains(realmAdmins, userId);
+        return realmAdmins.contains(userId);
     }
 
     @Override
     public void addRealmAdmin(UserModel agent) {
         UserData userData = ((UserAdapter)agent).getUser();
 
-        String[] currentAdmins = realm.getRealmAdmins();
-        String[] newAdmins = Utils.addItemToArray(currentAdmins, userData.getId());
-
-        realm.setRealmAdmins(newAdmins);
-        updateRealm();
+        noSQL.pushItemToList(realm, "realmAdmins", userData.getId());
     }
 
     @Override
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/RealmData.java b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/RealmData.java
index 42f8ab2..8152c5b 100644
--- a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/RealmData.java
+++ b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/RealmData.java
@@ -1,6 +1,7 @@
 package org.keycloak.services.models.nosql.keycloak.data;
 
 import java.security.SecureRandom;
+import java.util.List;
 import java.util.Random;
 import java.util.UUID;
 
@@ -32,8 +33,8 @@ public class RealmData implements NoSQLObject {
     private String publicKeyPem;
     private String privateKeyPem;
 
-    private String[] defaultRoles;
-    private String[] realmAdmins;
+    private List<String> defaultRoles;
+    private List<String> realmAdmins;
 
     @NoSQLId
     public String getOid() {
@@ -44,7 +45,6 @@ public class RealmData implements NoSQLObject {
         this.oid = oid;
     }
 
-    // TODO: Is ID really needed? It seems that it exists just to workaround picketlink...
     @NoSQLField
     public String getId() {
         return id;
@@ -154,20 +154,20 @@ public class RealmData implements NoSQLObject {
     }
 
     @NoSQLField
-    public String[] getDefaultRoles() {
+    public List<String> getDefaultRoles() {
         return defaultRoles;
     }
 
-    public void setDefaultRoles(String[] defaultRoles) {
+    public void setDefaultRoles(List<String> defaultRoles) {
         this.defaultRoles = defaultRoles;
     }
 
     @NoSQLField
-    public String[] getRealmAdmins() {
+    public List<String> getRealmAdmins() {
         return realmAdmins;
     }
 
-    public void setRealmAdmins(String[] realmAdmins) {
+    public void setRealmAdmins(List<String> realmAdmins) {
         this.realmAdmins = realmAdmins;
     }
 
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/RoleData.java b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/RoleData.java
index 89138e6..49483a1 100644
--- a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/RoleData.java
+++ b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/RoleData.java
@@ -9,7 +9,6 @@ import org.keycloak.services.models.nosql.api.NoSQLField;
 import org.keycloak.services.models.nosql.api.NoSQLId;
 import org.keycloak.services.models.nosql.api.NoSQLObject;
 import org.keycloak.services.models.nosql.api.query.NoSQLQuery;
-import org.keycloak.services.models.nosql.impl.Utils;
 
 /**
  * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -81,10 +80,7 @@ public class RoleData implements NoSQLObject {
         List<UserData> users = noSQL.loadObjects(UserData.class, query);
         for (UserData user : users) {
             logger.info("Removing role " + getName() + " from user " + user.getLoginName());
-            String[] roleIds = user.getRoleIds();
-            String[] newRoleIds = Utils.removeItemFromArray(roleIds, getId());
-            user.setRoleIds(newRoleIds);
-            noSQL.saveObject(user);
+            noSQL.pullItemFromList(user, "roleIds", getId());
         }
 
         // Remove this scope from all users, which has it
@@ -95,10 +91,7 @@ public class RoleData implements NoSQLObject {
         users = noSQL.loadObjects(UserData.class, query);
         for (UserData user : users) {
             logger.info("Removing scope " + getName() + " from user " + user.getLoginName());
-            String[] scopeIds = user.getScopeIds();
-            String[] newScopeIds = Utils.removeItemFromArray(scopeIds, getId());
-            user.setScopeIds(newScopeIds);
-            noSQL.saveObject(user);
+            noSQL.pullItemFromList(user, "scopeIds", getId());
         }
 
     }
diff --git a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/UserData.java b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/UserData.java
index c2321fb..83c7151 100644
--- a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/UserData.java
+++ b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/UserData.java
@@ -1,5 +1,8 @@
 package org.keycloak.services.models.nosql.keycloak.data;
 
+import java.util.List;
+
+import org.jboss.resteasy.logging.Logger;
 import org.keycloak.services.models.nosql.api.AbstractAttributedNoSQLObject;
 import org.keycloak.services.models.nosql.api.NoSQL;
 import org.keycloak.services.models.nosql.api.NoSQLCollection;
@@ -14,6 +17,8 @@ import org.keycloak.services.models.nosql.keycloak.data.credentials.PasswordData
 @NoSQLCollection(collectionName = "users")
 public class UserData extends AbstractAttributedNoSQLObject {
 
+    private static final Logger logger = Logger.getLogger(UserData.class);
+
     private String id;
     private String loginName;
     private String firstName;
@@ -23,8 +28,8 @@ public class UserData extends AbstractAttributedNoSQLObject {
 
     private String realmId;
 
-    private String[] roleIds;
-    private String[] scopeIds;
+    private List<String> roleIds;
+    private List<String> scopeIds;
 
     @NoSQLId
     public String getId() {
@@ -90,20 +95,20 @@ public class UserData extends AbstractAttributedNoSQLObject {
     }
 
     @NoSQLField
-    public String[] getRoleIds() {
+    public List<String> getRoleIds() {
         return roleIds;
     }
 
-    public void setRoleIds(String[] roleIds) {
+    public void setRoleIds(List<String> roleIds) {
         this.roleIds = roleIds;
     }
 
     @NoSQLField
-    public String[] getScopeIds() {
+    public List<String> getScopeIds() {
         return scopeIds;
     }
 
-    public void setScopeIds(String[] scopeIds) {
+    public void setScopeIds(List<String> scopeIds) {
         this.scopeIds = scopeIds;
     }
 
@@ -116,5 +121,16 @@ public class UserData extends AbstractAttributedNoSQLObject {
         // Remove social links and passwords of this user
         noSQL.removeObjects(SocialLinkData.class, query);
         noSQL.removeObjects(PasswordData.class, query);
+
+        // Remove this user from all realms, which have him as an admin
+        NoSQLQuery realmQuery = noSQL.createQueryBuilder()
+                .andCondition("realmAdmins", id)
+                .build();
+
+        List<RealmData> realms = noSQL.loadObjects(RealmData.class, realmQuery);
+        for (RealmData realm : realms) {
+            logger.info("Removing admin user " + getLoginName() + " from realm " + realm.getId());
+            noSQL.pullItemFromList(realm, "realmAdmins", getId());
+        }
     }
 }
diff --git a/services/src/test/java/org/keycloak/test/nosql/Address.java b/services/src/test/java/org/keycloak/test/nosql/Address.java
new file mode 100644
index 0000000..8b56c59
--- /dev/null
+++ b/services/src/test/java/org/keycloak/test/nosql/Address.java
@@ -0,0 +1,43 @@
+package org.keycloak.test.nosql;
+
+import java.util.List;
+
+import org.keycloak.services.models.nosql.api.AbstractNoSQLObject;
+import org.keycloak.services.models.nosql.api.NoSQLField;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class Address extends AbstractNoSQLObject {
+
+    private String street;
+    private int number;
+    private List<String> flatNumbers;
+
+    @NoSQLField
+    public String getStreet() {
+        return street;
+    }
+
+    public void setStreet(String street) {
+        this.street = street;
+    }
+
+    @NoSQLField
+    public int getNumber() {
+        return number;
+    }
+
+    public void setNumber(int number) {
+        this.number = number;
+    }
+
+    @NoSQLField
+    public List<String> getFlatNumbers() {
+        return flatNumbers;
+    }
+
+    public void setFlatNumbers(List<String> flatNumbers) {
+        this.flatNumbers = flatNumbers;
+    }
+}
diff --git a/services/src/test/java/org/keycloak/test/nosql/MongoDBModelTest.java b/services/src/test/java/org/keycloak/test/nosql/MongoDBModelTest.java
new file mode 100644
index 0000000..276ea3c
--- /dev/null
+++ b/services/src/test/java/org/keycloak/test/nosql/MongoDBModelTest.java
@@ -0,0 +1,103 @@
+package org.keycloak.test.nosql;
+
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import com.mongodb.DB;
+import com.mongodb.MongoClient;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.services.models.nosql.api.NoSQL;
+import org.keycloak.services.models.nosql.api.NoSQLObject;
+import org.keycloak.services.models.nosql.api.query.NoSQLQuery;
+import org.keycloak.services.models.nosql.impl.MongoDBImpl;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MongoDBModelTest {
+
+    private static final Class<? extends NoSQLObject>[] MANAGED_DATA_TYPES = (Class<? extends NoSQLObject>[])new Class<?>[] {
+            Person.class,
+            Address.class,
+    };
+
+    private MongoClient mongoClient;
+    private NoSQL mongoDB;
+
+    @Before
+    public void before() throws Exception {
+        try {
+            // TODO: authentication support
+            mongoClient = new MongoClient("localhost", 27017);
+
+            DB db = mongoClient.getDB("keycloakTest");
+            mongoDB = new MongoDBImpl(db, true, MANAGED_DATA_TYPES);
+
+        } catch (UnknownHostException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @After
+    public void after() throws Exception {
+        mongoClient.close();
+    }
+
+    // @Test
+    public void mongoModelTest() throws Exception {
+        // Add some user
+        Person john = new Person();
+        john.setFirstName("john");
+        john.setAge(25);
+
+        mongoDB.saveObject(john);
+
+        // Add another user
+        Person mary = new Person();
+        mary.setFirstName("mary");
+        mary.setKids(Arrays.asList(new String[] {"Peter", "Paul", "Wendy"}));
+
+        Address addr1 = new Address();
+        addr1.setStreet("Elm");
+        addr1.setNumber(5);
+        addr1.setFlatNumbers(Arrays.asList(new String[] {"flat1", "flat2"}));
+        Address addr2 = new Address();
+        List<Address> addresses = new ArrayList<Address>();
+        addresses.add(addr1);
+        addresses.add(addr2);
+
+        mary.setAddresses(addresses);
+        mongoDB.saveObject(mary);
+
+        Assert.assertEquals(2, mongoDB.loadObjects(Person.class, mongoDB.createQueryBuilder().build()).size());
+
+        NoSQLQuery query = mongoDB.createQueryBuilder().andCondition("addresses.flatNumbers", "flat1").build();
+        List<Person> persons = mongoDB.loadObjects(Person.class, query);
+        Assert.assertEquals(1, persons.size());
+        mary = persons.get(0);
+        Assert.assertEquals(mary.getFirstName(), "mary");
+        Assert.assertTrue(mary.getKids().contains("Paul"));
+        Assert.assertEquals(2, mary.getAddresses().size());
+        Assert.assertEquals(Address.class, mary.getAddresses().get(0).getClass());
+
+        // Test push/pull
+        mongoDB.pushItemToList(mary, "kids", "Pauline");
+        mongoDB.pullItemFromList(mary, "kids", "Paul");
+
+        Address addr3 = new Address();
+        addr3.setNumber(6);
+        addr3.setStreet("Broadway");
+        mongoDB.pushItemToList(mary, "addresses", addr3);
+
+        mary = mongoDB.loadObject(Person.class, mary.getId());
+        Assert.assertEquals(3, mary.getKids().size());
+        Assert.assertTrue(mary.getKids().contains("Pauline"));
+        Assert.assertFalse(mary.getKids().contains("Paul"));
+        Assert.assertEquals(3, mary.getAddresses().size());
+    }
+}
diff --git a/services/src/test/java/org/keycloak/test/nosql/Person.java b/services/src/test/java/org/keycloak/test/nosql/Person.java
new file mode 100644
index 0000000..fa091fa
--- /dev/null
+++ b/services/src/test/java/org/keycloak/test/nosql/Person.java
@@ -0,0 +1,66 @@
+package org.keycloak.test.nosql;
+
+import java.util.List;
+
+import org.keycloak.services.models.nosql.api.AbstractNoSQLObject;
+import org.keycloak.services.models.nosql.api.NoSQLCollection;
+import org.keycloak.services.models.nosql.api.NoSQLField;
+import org.keycloak.services.models.nosql.api.NoSQLId;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@NoSQLCollection(collectionName = "persons")
+public class Person extends AbstractNoSQLObject {
+
+    private String id;
+    private String firstName;
+    private int age;
+    private List<String> kids;
+    private List<Address> addresses;
+
+    @NoSQLId
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    @NoSQLField
+    public String getFirstName() {
+        return firstName;
+    }
+
+    public void setFirstName(String firstName) {
+        this.firstName = firstName;
+    }
+
+    @NoSQLField
+    public int getAge() {
+        return age;
+    }
+
+    public void setAge(int age) {
+        this.age = age;
+    }
+
+    @NoSQLField
+    public List<String> getKids() {
+        return kids;
+    }
+
+    public void setKids(List<String> kids) {
+        this.kids = kids;
+    }
+
+    @NoSQLField
+    public List<Address> getAddresses() {
+        return addresses;
+    }
+
+    public void setAddresses(List<Address> addresses) {
+        this.addresses = addresses;
+    }
+}