diff --git a/services/src/main/java/org/keycloak/services/models/nosql/api/AbstractAttributedNoSQLObject.java b/services/src/main/java/org/keycloak/services/models/nosql/api/AbstractAttributedNoSQLObject.java index efdddee67c..a3a6ddb1bf 100644 --- a/services/src/main/java/org/keycloak/services/models/nosql/api/AbstractAttributedNoSQLObject.java +++ b/services/src/main/java/org/keycloak/services/models/nosql/api/AbstractAttributedNoSQLObject.java @@ -7,7 +7,7 @@ import java.util.Map; /** * @author Marek Posolda */ -public abstract class AbstractAttributedNoSQLObject implements AttributedNoSQLObject { +public abstract class AbstractAttributedNoSQLObject extends AbstractNoSQLObject implements AttributedNoSQLObject { // Simple hashMap for now (no thread-safe) private Map attributes = new HashMap(); diff --git a/services/src/main/java/org/keycloak/services/models/nosql/api/AbstractNoSQLObject.java b/services/src/main/java/org/keycloak/services/models/nosql/api/AbstractNoSQLObject.java new file mode 100644 index 0000000000..692839bc00 --- /dev/null +++ b/services/src/main/java/org/keycloak/services/models/nosql/api/AbstractNoSQLObject.java @@ -0,0 +1,12 @@ +package org.keycloak.services.models.nosql.api; + +/** + * @author Marek Posolda + */ +public abstract class AbstractNoSQLObject implements NoSQLObject { + + @Override + public void afterRemove(NoSQL noSQL) { + // Empty by default + } +} 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 c3081b05a9..1b28fd1e33 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 @@ -3,6 +3,7 @@ package org.keycloak.services.models.nosql.api; import java.util.List; import org.keycloak.services.models.nosql.api.query.NoSQLQuery; +import org.keycloak.services.models.nosql.api.query.NoSQLQueryBuilder; /** * @author Marek Posolda @@ -26,4 +27,6 @@ public interface NoSQL { void removeObject(Class type, String oid); void removeObjects(Class type, NoSQLQuery query); + + NoSQLQueryBuilder createQueryBuilder(); } diff --git a/services/src/main/java/org/keycloak/services/models/nosql/api/NoSQLField.java b/services/src/main/java/org/keycloak/services/models/nosql/api/NoSQLField.java index c3e0586d2f..8c5d2a2940 100644 --- a/services/src/main/java/org/keycloak/services/models/nosql/api/NoSQLField.java +++ b/services/src/main/java/org/keycloak/services/models/nosql/api/NoSQLField.java @@ -16,7 +16,5 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; @Retention(RUNTIME) public @interface NoSQLField { - String fieldName() default ""; - - // TODO: add lazy loading? + // TODO: fieldName add lazy loading? } diff --git a/services/src/main/java/org/keycloak/services/models/nosql/api/NoSQLObject.java b/services/src/main/java/org/keycloak/services/models/nosql/api/NoSQLObject.java index 1f430b6f5a..7d31b68700 100644 --- a/services/src/main/java/org/keycloak/services/models/nosql/api/NoSQLObject.java +++ b/services/src/main/java/org/keycloak/services/models/nosql/api/NoSQLObject.java @@ -1,9 +1,16 @@ package org.keycloak.services.models.nosql.api; /** - * Just marker interface + * Base interface for object, which is persisted in NoSQL database * * @author Marek Posolda */ public interface NoSQLObject { + + /** + * Lifecycle callback, which is called after removal of this object from NoSQL database. + * It may be useful for triggering removal of wired objects. + */ + void afterRemove(NoSQL noSQL); + } 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 aa4eb48e52..341a115120 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 @@ -12,14 +12,6 @@ public abstract class NoSQLQueryBuilder { protected NoSQLQueryBuilder() {}; - public static NoSQLQueryBuilder create(Class builderClass) { - try { - return builderClass.newInstance(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - public NoSQLQuery build() { return new NoSQLQuery(queryAttributes); } diff --git a/services/src/main/java/org/keycloak/services/models/nosql/api/types/ConverterKey.java b/services/src/main/java/org/keycloak/services/models/nosql/api/types/ConverterKey.java index f4b6fe0cdf..68fc3cb950 100644 --- a/services/src/main/java/org/keycloak/services/models/nosql/api/types/ConverterKey.java +++ b/services/src/main/java/org/keycloak/services/models/nosql/api/types/ConverterKey.java @@ -27,4 +27,5 @@ class ConverterKey { ConverterKey tc = (ConverterKey)obj; return tc.applicationObjectType.equals(this.applicationObjectType) && tc.dbObjectType.equals(this.dbObjectType); } + } 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 7bdb384625..3368e4f390 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 @@ -11,6 +11,7 @@ import java.util.Map; */ public class TypeConverter { + // TODO: Thread-safety support (maybe...) private Map> converterRegistry = new HashMap>(); public void addConverter(Converter converter) { @@ -18,14 +19,19 @@ public class TypeConverter { converterRegistry.put(converterKey, converter); } - public T convertDBObjectToApplicationObject(S dbObject, Class expectedApplicationObjectType, Class expectedDBObjectType) { + public T convertDBObjectToApplicationObject(S dbObject, Class 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 expectedDBObjectType = (Class)dbObject.getClass(); Converter converter = getConverter(expectedApplicationObjectType, expectedDBObjectType); return converter.convertDBObjectToApplicationObject(dbObject); } - public S convertApplicationObjectToDBObject(T applicationobject, Class expectedApplicationObjectType, Class expectedDBObjectType) { + public S convertApplicationObjectToDBObject(T applicationObject, Class 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 expectedApplicationObjectType = (Class)applicationObject.getClass(); Converter converter = getConverter(expectedApplicationObjectType, expectedDBObjectType); - return converter.convertApplicationObjectToDBObject(applicationobject); + + return converter.convertApplicationObjectToDBObject(applicationObject); } private Converter getConverter( Class expectedApplicationObjectType, Class expectedDBObjectType) { 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 fd6efa7627..0385aeef0c 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 @@ -12,20 +12,21 @@ import com.mongodb.DBCollection; import com.mongodb.DBCursor; import com.mongodb.DBObject; import org.bson.types.ObjectId; -import org.keycloak.services.models.nosql.api.AttributedNoSQLObject; +import org.jboss.resteasy.logging.Logger; import org.keycloak.services.models.nosql.api.NoSQL; import org.keycloak.services.models.nosql.api.NoSQLCollection; 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.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.NoSQLObjectConverter; import org.picketlink.common.properties.Property; import org.picketlink.common.properties.query.AnnotatedPropertyCriteria; import org.picketlink.common.properties.query.PropertyQueries; -import org.picketlink.common.reflection.Types; /** * @author Marek Posolda @@ -33,59 +34,61 @@ import org.picketlink.common.reflection.Types; public class MongoDBImpl implements NoSQL { private final DB database; - // private static final Logger logger = Logger.getLogger(MongoDBImpl.class); + private static final Logger logger = Logger.getLogger(MongoDBImpl.class); private final TypeConverter typeConverter; + private ConcurrentMap, ObjectInfo> objectInfoCache = + new ConcurrentHashMap, ObjectInfo>(); - public MongoDBImpl(DB database) { + public MongoDBImpl(DB database, boolean removeAllObjectsAtStartup, Class[] managedDataTypes) { this.database = database; typeConverter = new TypeConverter(); typeConverter.addConverter(new BasicDBListToStringArrayConverter()); + for (Class type : managedDataTypes) { + typeConverter.addConverter(new NoSQLObjectConverter(this, typeConverter, type)); + getObjectInfo(type); + } + + if (removeAllObjectsAtStartup) { + for (Class type : managedDataTypes) { + ObjectInfo objectInfo = getObjectInfo(type); + String collectionName = objectInfo.getDbCollectionName(); + if (collectionName != null) { + logger.debug("Removing all objects of type " + type); + + DBCollection dbCollection = this.database.getCollection(collectionName); + dbCollection.remove(new BasicDBObject()); + } else { + logger.debug("Skip removing objects of type " + type + " as it doesn't have it's own collection"); + } + } + logger.info("All objects successfully removed from MongoDB"); + } } - private ConcurrentMap, ObjectInfo> objectInfoCache = - new ConcurrentHashMap, ObjectInfo>(); - - - @Override public void saveObject(NoSQLObject object) { - Class clazz = object.getClass(); + Class clazz = object.getClass(); // Find annotations for ID, for all the properties and for the name of the collection. ObjectInfo objectInfo = getObjectInfo(clazz); // Create instance of BasicDBObject and add all declared properties to it (properties with null value probably should be skipped) - BasicDBObject dbObject = new BasicDBObject(); - List> props = objectInfo.getProperties(); - for (Property property : props) { - String propName = property.getName(); - Object propValue = property.getValue(object); - - - dbObject.append(propName, propValue); - } - - // Adding attributes - if (object instanceof AttributedNoSQLObject) { - AttributedNoSQLObject attributedObject = (AttributedNoSQLObject)object; - Map attributes = attributedObject.getAttributes(); - for (Map.Entry attribute : attributes.entrySet()) { - dbObject.append(attribute.getKey(), attribute.getValue()); - } - } + BasicDBObject dbObject = typeConverter.convertApplicationObjectToDBObject(object, BasicDBObject.class); DBCollection dbCollection = database.getCollection(objectInfo.getDbCollectionName()); // Decide if we should insert or update (based on presence of oid property in original object) Property oidProperty = objectInfo.getOidProperty(); - String currentId = oidProperty.getValue(object); + String currentId = oidProperty == null ? null : oidProperty.getValue(object); if (currentId == null) { dbCollection.insert(dbObject); // Add oid to value of given object - oidProperty.setValue(object, dbObject.getString("_id")); + if (oidProperty != null) { + oidProperty.setValue(object, dbObject.getString("_id")); + } } else { BasicDBObject setCommand = new BasicDBObject("$set", dbObject); BasicDBObject query = new BasicDBObject("_id", new ObjectId(currentId)); @@ -100,7 +103,7 @@ public class MongoDBImpl implements NoSQL { BasicDBObject idQuery = new BasicDBObject("_id", new ObjectId(oid)); DBObject dbObject = dbCollection.findOne(idQuery); - return convertObject(type, dbObject); + return typeConverter.convertDBObjectToApplicationObject(dbObject, type); } @Override @@ -129,7 +132,7 @@ public class MongoDBImpl implements NoSQL { @Override public void removeObject(NoSQLObject object) { Class type = object.getClass(); - ObjectInfo objectInfo = getObjectInfo(type); + ObjectInfo objectInfo = getObjectInfo(type); Property idProperty = objectInfo.getOidProperty(); String oid = idProperty.getValue(object); @@ -139,18 +142,39 @@ public class MongoDBImpl implements NoSQL { @Override public void removeObject(Class type, String oid) { - DBCollection dbCollection = getDBCollectionForType(type); + NoSQLObject found = loadObject(type, oid); + if (found == null) { + logger.warn("Object of type: " + type + ", oid: " + oid + " doesn't exist in MongoDB. Skip removal"); + } else { + DBCollection dbCollection = getDBCollectionForType(type); + BasicDBObject dbQuery = new BasicDBObject("_id", new ObjectId(oid)); + dbCollection.remove(dbQuery); + logger.info("Object of type: " + type + ", oid: " + oid + " removed from MongoDB."); - BasicDBObject dbQuery = new BasicDBObject("_id", new ObjectId(oid)); - dbCollection.remove(dbQuery); + found.afterRemove(this); + } } @Override public void removeObjects(Class type, NoSQLQuery query) { - DBCollection dbCollection = getDBCollectionForType(type); - BasicDBObject dbQuery = getDBQueryFromQuery(query); + List foundObjects = loadObjects(type, query); + if (foundObjects.size() == 0) { + logger.info("Not found any objects of type: " + type + ", query: " + query); + } else { + DBCollection dbCollection = getDBCollectionForType(type); + BasicDBObject dbQuery = getDBQueryFromQuery(query); + dbCollection.remove(dbQuery); + logger.info("Removed " + foundObjects.size() + " objects of type: " + type + ", query: " + query); - dbCollection.remove(dbQuery); + for (NoSQLObject found : foundObjects) { + found.afterRemove(this); + } + } + } + + @Override + public NoSQLQueryBuilder createQueryBuilder() { + return new MongoDBQueryBuilder(); } // Possibility to add user-defined converters @@ -158,26 +182,19 @@ public class MongoDBImpl implements NoSQL { typeConverter.addConverter(converter); } - private ObjectInfo getObjectInfo(Class objectClass) { - ObjectInfo objectInfo = (ObjectInfo)objectInfoCache.get(objectClass); + public ObjectInfo getObjectInfo(Class objectClass) { + ObjectInfo objectInfo = objectInfoCache.get(objectClass); if (objectInfo == null) { Property idProperty = PropertyQueries.createQuery(objectClass).addCriteria(new AnnotatedPropertyCriteria(NoSQLId.class)).getFirstResult(); - if (idProperty == null) { - // TODO: should be allowed to have NoSQLObject classes without declared NoSQLId annotation? - throw new IllegalStateException("Class " + objectClass + " doesn't have property with declared annotation " + NoSQLId.class); - } List> properties = PropertyQueries.createQuery(objectClass).addCriteria(new AnnotatedPropertyCriteria(NoSQLField.class)).getResultList(); NoSQLCollection classAnnotation = objectClass.getAnnotation(NoSQLCollection.class); - if (classAnnotation == null) { - throw new IllegalStateException("Class " + objectClass + " doesn't have annotation " + NoSQLCollection.class); - } - String dbCollectionName = classAnnotation.collectionName(); - objectInfo = new ObjectInfo((Class)objectClass, dbCollectionName, idProperty, properties); + String dbCollectionName = classAnnotation==null ? null : classAnnotation.collectionName(); + objectInfo = new ObjectInfo(objectClass, dbCollectionName, idProperty, properties); - ObjectInfo existing = objectInfoCache.putIfAbsent((Class)objectClass, objectInfo); + ObjectInfo existing = objectInfoCache.putIfAbsent(objectClass, objectInfo); if (existing != null) { objectInfo = existing; } @@ -186,75 +203,23 @@ public class MongoDBImpl implements NoSQL { return objectInfo; } - - private T convertObject(Class type, DBObject dbObject) { - if (dbObject == null) { - return null; - } - - ObjectInfo objectInfo = getObjectInfo(type); - - T object; - try { - object = type.newInstance(); - } catch (Exception e) { - throw new RuntimeException(e); - } - - for (String key : dbObject.keySet()) { - Object value = dbObject.get(key); - Property property; - - if ("_id".equals(key)) { - // Current property is "id" - Property idProperty = objectInfo.getOidProperty(); - idProperty.setValue(object, value.toString()); - - } else if ((property = objectInfo.getPropertyByName(key)) != null) { - // It's declared property with @DBField annotation - Class expectedType = property.getJavaClass(); - Class actualType = value != null ? value.getClass() : expectedType; - - // handle primitives - expectedType = Types.boxedClass(expectedType); - actualType = Types.boxedClass(actualType); - - if (actualType.isAssignableFrom(expectedType)) { - property.setValue(object, value); - } else { - // we need to convert - Object convertedValue = typeConverter.convertDBObjectToApplicationObject(value, expectedType, actualType); - property.setValue(object, convertedValue); - } - - } 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 " + type); - } - } - - return object; - } - private List convertCursor(Class type, DBCursor cursor) { List result = new ArrayList(); - for (DBObject dbObject : cursor) { - T converted = convertObject(type, dbObject); - result.add(converted); + try { + for (DBObject dbObject : cursor) { + T converted = typeConverter.convertDBObjectToApplicationObject(dbObject, type); + result.add(converted); + } + } finally { + cursor.close(); } return result; } private DBCollection getDBCollectionForType(Class type) { - ObjectInfo objectInfo = getObjectInfo(type); + ObjectInfo objectInfo = getObjectInfo(type); return database.getCollection(objectInfo.getDbCollectionName()); } 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 b3af7324eb..94b6196dbc 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 @@ -9,6 +9,8 @@ import org.keycloak.services.models.nosql.api.query.NoSQLQueryBuilder; */ public class MongoDBQueryBuilder extends NoSQLQueryBuilder { + protected MongoDBQueryBuilder() {}; + @Override public NoSQLQueryBuilder inCondition(String name, Object[] values) { if (values == null) { 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 867ac12b62..27f782c7e2 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 @@ -8,9 +8,9 @@ import org.picketlink.common.properties.Property; /** * @author Marek Posolda */ -class ObjectInfo { +public class ObjectInfo { - private final Class objectClass; + private final Class objectClass; private final String dbCollectionName; @@ -18,14 +18,14 @@ class ObjectInfo { private final List> properties; - public ObjectInfo(Class objectClass, String dbCollectionName, Property oidProperty, List> properties) { + public ObjectInfo(Class objectClass, String dbCollectionName, Property oidProperty, List> properties) { this.objectClass = objectClass; this.dbCollectionName = dbCollectionName; this.oidProperty = oidProperty; this.properties = properties; } - public Class getObjectClass() { + public Class getObjectClass() { return objectClass; } diff --git a/services/src/main/java/org/keycloak/services/models/nosql/impl/Utils.java b/services/src/main/java/org/keycloak/services/models/nosql/impl/Utils.java index 1398e278b8..26ab30a87c 100644 --- a/services/src/main/java/org/keycloak/services/models/nosql/impl/Utils.java +++ b/services/src/main/java/org/keycloak/services/models/nosql/impl/Utils.java @@ -53,4 +53,23 @@ public class Utils { return false; } + public static T[] removeItemFromArray(T[] inputArray, T item) { + if (item == null) { + throw new IllegalArgumentException("item must be non-null"); + } + + if (inputArray == null) { + return inputArray; + } else { + T[] outputArray = (T[])Array.newInstance(item.getClass(), inputArray.length - 1); + int counter = 0; + for (T object : inputArray) { + if (!item.equals(object)) { + outputArray[counter++] = object; + } + } + return outputArray; + } + } + } diff --git a/services/src/main/java/org/keycloak/services/models/nosql/impl/types/BasicDBListToStringArrayConverter.java b/services/src/main/java/org/keycloak/services/models/nosql/impl/types/BasicDBListToStringArrayConverter.java index 0f9d5d5917..0c3f176743 100644 --- a/services/src/main/java/org/keycloak/services/models/nosql/impl/types/BasicDBListToStringArrayConverter.java +++ b/services/src/main/java/org/keycloak/services/models/nosql/impl/types/BasicDBListToStringArrayConverter.java @@ -13,7 +13,7 @@ public class BasicDBListToStringArrayConverter implements ConverterMarek Posolda + */ +public class NoSQLObjectConverter implements Converter { + + private final MongoDBImpl mongoDBImpl; + private final TypeConverter typeConverter; + private final Class expectedNoSQLObjectType; + + public NoSQLObjectConverter(MongoDBImpl mongoDBImpl, TypeConverter typeConverter, Class expectedNoSQLObjectType) { + this.mongoDBImpl = mongoDBImpl; + this.typeConverter = typeConverter; + this.expectedNoSQLObjectType = expectedNoSQLObjectType; + } + + @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 property; + + if ("_id".equals(key)) { + // Current property is "id" + Property 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) { + 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> props = objectInfo.getProperties(); + for (Property 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); + } + + // Adding attributes + if (applicationObject instanceof AttributedNoSQLObject) { + AttributedNoSQLObject attributedObject = (AttributedNoSQLObject)applicationObject; + Map attributes = attributedObject.getAttributes(); + for (Map.Entry attribute : attributes.entrySet()) { + dbObject.append(attribute.getKey(), attribute.getValue()); + } + } + + return dbObject; + } + + @Override + public Class getApplicationObjectType() { + return expectedNoSQLObjectType; + } + + @Override + public Class getDBObjectType() { + return BasicDBObject.class; + } +} 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 fc8a6e177f..dd1040094c 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 @@ -94,7 +94,7 @@ public class ApplicationAdapter implements ApplicationModel { @Override public RoleAdapter getRole(String name) { - NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class) + NoSQLQuery query = noSQL.createQueryBuilder() .andCondition("name", name) .andCondition("applicationId", getId()) .build(); @@ -122,7 +122,7 @@ public class ApplicationAdapter implements ApplicationModel { @Override public List getRoles() { - NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class) + NoSQLQuery query = noSQL.createQueryBuilder() .andCondition("applicationId", getId()) .build(); List roles = noSQL.loadObjects(RoleData.class, query); @@ -142,7 +142,7 @@ public class ApplicationAdapter implements ApplicationModel { Set result = new HashSet(); - NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class) + NoSQLQuery query = noSQL.createQueryBuilder() .inCondition("_id", roleIds) .build(); List roles = noSQL.loadObjects(RoleData.class, query); @@ -184,7 +184,7 @@ public class ApplicationAdapter implements ApplicationModel { Set result = new HashSet(); - NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class) + NoSQLQuery query = noSQL.createQueryBuilder() .inCondition("_id", scopeIds) .build(); List roles = noSQL.loadObjects(RoleData.class, query); diff --git a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/adapters/MongoDBSessionFactory.java b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/adapters/MongoDBSessionFactory.java index a0ff43589e..114f7b74db 100644 --- a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/adapters/MongoDBSessionFactory.java +++ b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/adapters/MongoDBSessionFactory.java @@ -4,6 +4,8 @@ import java.net.UnknownHostException; import com.mongodb.DB; import com.mongodb.MongoClient; +import org.jboss.resteasy.logging.Logger; +import org.keycloak.services.managers.RealmManager; import org.keycloak.services.models.KeycloakSession; import org.keycloak.services.models.KeycloakSessionFactory; import org.keycloak.services.models.nosql.api.NoSQL; @@ -26,8 +28,9 @@ import org.keycloak.services.models.nosql.keycloak.data.credentials.PasswordData * @author Marek Posolda */ public class MongoDBSessionFactory implements KeycloakSessionFactory { + protected static final Logger logger = Logger.getLogger(MongoDBSessionFactory.class); - private static final Class[] MANAGED_DATA_TYPES = { + private static final Class[] MANAGED_DATA_TYPES = (Class[])new Class[] { RealmData.class, UserData.class, RoleData.class, @@ -41,25 +44,17 @@ public class MongoDBSessionFactory implements KeycloakSessionFactory { private final NoSQL mongoDB; public MongoDBSessionFactory(String host, int port, String dbName, boolean removeAllObjectsAtStartup) { + logger.info(String.format("Going to use MongoDB database. host: %s, port: %d, databaseName: %s, removeAllObjectsAtStartup: %b", host, port, dbName, removeAllObjectsAtStartup)); try { // TODO: authentication support mongoClient = new MongoClient(host, port); DB db = mongoClient.getDB(dbName); - mongoDB = new MongoDBImpl(db); + mongoDB = new MongoDBImpl(db, removeAllObjectsAtStartup, MANAGED_DATA_TYPES); } catch (UnknownHostException e) { throw new RuntimeException(e); } - - if (removeAllObjectsAtStartup) { - NoSQLQuery emptyQuery = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class).build(); - for (Class type : MANAGED_DATA_TYPES) { - mongoDB.removeObjects((Class)type, emptyQuery); - } - // TODO: logging - System.out.println("All objects successfully removed from DB"); - } } @Override @@ -69,6 +64,7 @@ public class MongoDBSessionFactory implements KeycloakSessionFactory { @Override public void close() { + logger.info("Closing MongoDB client"); mongoClient.close(); } } diff --git a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/adapters/NoSQLSession.java b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/adapters/NoSQLSession.java index 9e2619ec23..838a6a4b60 100644 --- a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/adapters/NoSQLSession.java +++ b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/adapters/NoSQLSession.java @@ -55,7 +55,7 @@ public class NoSQLSession implements KeycloakSession { @Override public RealmModel getRealm(String id) { - NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class) + NoSQLQuery query = noSQL.createQueryBuilder() .andCondition("id", id) .build(); RealmData realmData = noSQL.loadSingleObject(RealmData.class, query); @@ -65,7 +65,7 @@ public class NoSQLSession implements KeycloakSession { @Override public List getRealms(UserModel admin) { String userId = ((UserAdapter)admin).getUser().getId(); - NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class) + NoSQLQuery query = noSQL.createQueryBuilder() .andCondition("realmAdmins", userId) .build(); List realms = noSQL.loadObjects(RealmData.class, query); 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 5181c42f72..f7e3e83e02 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 @@ -250,7 +250,7 @@ public class RealmAdapter implements RealmModel { @Override public UserAdapter getUser(String name) { - NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class) + NoSQLQuery query = noSQL.createQueryBuilder() .andCondition("loginName", name) .andCondition("realmId", getOid()) .build(); @@ -280,7 +280,7 @@ public class RealmAdapter implements RealmModel { @Override public RoleAdapter getRole(String name) { - NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class) + NoSQLQuery query = noSQL.createQueryBuilder() .andCondition("name", name) .andCondition("realmId", getOid()) .build(); @@ -308,7 +308,7 @@ public class RealmAdapter implements RealmModel { @Override public List getRoles() { - NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class) + NoSQLQuery query = noSQL.createQueryBuilder() .andCondition("realmId", getOid()) .build(); List roles = noSQL.loadObjects(RoleData.class, query); @@ -325,7 +325,7 @@ public class RealmAdapter implements RealmModel { public List getDefaultRoles() { String[] defaultRoles = realm.getDefaultRoles(); - NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class) + NoSQLQuery query = noSQL.createQueryBuilder() .inCondition("_id", defaultRoles) .build(); List defaultRolesData = noSQL.loadObjects(RoleData.class, query); @@ -393,7 +393,7 @@ public class RealmAdapter implements RealmModel { @Override public List getApplications() { - NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class) + NoSQLQuery query = noSQL.createQueryBuilder() .andCondition("realmId", getOid()) .build(); List appDatas = noSQL.loadObjects(ApplicationData.class, query); @@ -456,7 +456,7 @@ public class RealmAdapter implements RealmModel { Set result = new HashSet(); - NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class) + NoSQLQuery query = noSQL.createQueryBuilder() .inCondition("_id", roleIds) .build(); List roles = noSQL.loadObjects(RoleData.class, query); @@ -492,7 +492,7 @@ public class RealmAdapter implements RealmModel { Set result = new HashSet(); - NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class) + NoSQLQuery query = noSQL.createQueryBuilder() .inCondition("_id", scopeIds) .build(); List roles = noSQL.loadObjects(RoleData.class, query); @@ -639,7 +639,7 @@ public class RealmAdapter implements RealmModel { } protected List getRequiredCredentialsData(int credentialType) { - NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class) + NoSQLQuery query = noSQL.createQueryBuilder() .andCondition("realmId", getOid()) .andCondition("clientType", credentialType) .build(); @@ -681,7 +681,7 @@ public class RealmAdapter implements RealmModel { @Override public UserModel getUserBySocialLink(SocialLinkModel socialLink) { - NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class) + NoSQLQuery query = noSQL.createQueryBuilder() .andCondition("socialProvider", socialLink.getSocialProvider()) .andCondition("socialUsername", socialLink.getSocialUsername()) .build(); @@ -701,7 +701,7 @@ public class RealmAdapter implements RealmModel { UserData userData = ((UserAdapter)user).getUser(); String userId = userData.getId(); - NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class) + NoSQLQuery query = noSQL.createQueryBuilder() .andCondition("userId", userId) .build(); List dbSocialLinks = noSQL.loadObjects(SocialLinkData.class, query); @@ -729,7 +729,7 @@ public class RealmAdapter implements RealmModel { public void removeSocialLink(UserModel user, SocialLinkModel socialLink) { UserData userData = ((UserAdapter)user).getUser(); String userId = userData.getId(); - NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class) + NoSQLQuery query = noSQL.createQueryBuilder() .andCondition("socialProvider", socialLink.getSocialProvider()) .andCondition("socialUsername", socialLink.getSocialUsername()) .andCondition("userId", userId) diff --git a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/credentials/PasswordCredentialHandler.java b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/credentials/PasswordCredentialHandler.java index 32a84d7469..77099873dd 100644 --- a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/credentials/PasswordCredentialHandler.java +++ b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/credentials/PasswordCredentialHandler.java @@ -57,7 +57,7 @@ public class PasswordCredentialHandler { // If the user for the provided username cannot be found we fail validation if (user != null) { if (user.isEnabled()) { - NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class) + NoSQLQuery query = noSQL.createQueryBuilder() .andCondition("userId", user.getId()) .build(); PasswordData passwordData = noSQL.loadSingleObject(PasswordData.class, query); @@ -87,7 +87,7 @@ public class PasswordCredentialHandler { Date effectiveDate, Date expiryDate) { // Try to look if user already has password - NoSQLQuery query = NoSQLQueryBuilder.create(MongoDBQueryBuilder.class) + NoSQLQuery query = noSQL.createQueryBuilder() .andCondition("userId", user.getId()) .build(); diff --git a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/ApplicationData.java b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/ApplicationData.java index 7035f9ae29..108f1d0e51 100644 --- a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/ApplicationData.java +++ b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/ApplicationData.java @@ -1,9 +1,11 @@ package org.keycloak.services.models.nosql.keycloak.data; +import org.keycloak.services.models.nosql.api.NoSQL; import org.keycloak.services.models.nosql.api.NoSQLCollection; 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; /** * @author Marek Posolda @@ -82,4 +84,16 @@ public class ApplicationData implements NoSQLObject { public void setRealmId(String realmId) { this.realmId = realmId; } + + @Override + public void afterRemove(NoSQL noSQL) { + // Remove resourceUser of this application + noSQL.removeObject(UserData.class, resourceUserId); + + // Remove all roles, which belongs to this application + NoSQLQuery query = noSQL.createQueryBuilder() + .andCondition("applicationId", id) + .build(); + noSQL.removeObjects(RoleData.class, query); + } } 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 a550c6a4b2..42f8ab246e 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 @@ -4,10 +4,12 @@ import java.security.SecureRandom; import java.util.Random; import java.util.UUID; +import org.keycloak.services.models.nosql.api.NoSQL; import org.keycloak.services.models.nosql.api.NoSQLCollection; 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; /** * @author Marek Posolda @@ -169,4 +171,22 @@ public class RealmData implements NoSQLObject { this.realmAdmins = realmAdmins; } + @Override + public void afterRemove(NoSQL noSQL) { + NoSQLQuery query = noSQL.createQueryBuilder() + .andCondition("realmId", oid) + .build(); + + // Remove all users of this realm + noSQL.removeObjects(UserData.class, query); + + // Remove all requiredCredentials of this realm + noSQL.removeObjects(RequiredCredentialData.class, query); + + // Remove all roles of this realm + noSQL.removeObjects(RoleData.class, query); + + // Remove all applications of this realm + noSQL.removeObjects(ApplicationData.class, query); + } } diff --git a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/RequiredCredentialData.java b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/RequiredCredentialData.java index d20b595b6a..124e507113 100644 --- a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/RequiredCredentialData.java +++ b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/RequiredCredentialData.java @@ -1,5 +1,6 @@ package org.keycloak.services.models.nosql.keycloak.data; +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; @@ -9,7 +10,7 @@ import org.keycloak.services.models.nosql.api.NoSQLObject; * @author Marek Posolda */ @NoSQLCollection(collectionName = "requiredCredentials") -public class RequiredCredentialData implements NoSQLObject { +public class RequiredCredentialData extends AbstractNoSQLObject { public static final int CLIENT_TYPE_USER = 1; public static final int CLIENT_TYPE_RESOURCE = 2; 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 9e00ef19fd..89138e68c0 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 @@ -1,9 +1,15 @@ 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.NoSQL; import org.keycloak.services.models.nosql.api.NoSQLCollection; 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 Marek Posolda @@ -11,6 +17,8 @@ import org.keycloak.services.models.nosql.api.NoSQLObject; @NoSQLCollection(collectionName = "roles") public class RoleData implements NoSQLObject { + private static final Logger logger = Logger.getLogger(RoleData.class); + private String id; private String name; private String description; @@ -62,4 +70,36 @@ public class RoleData implements NoSQLObject { public void setApplicationId(String applicationId) { this.applicationId = applicationId; } + + @Override + public void afterRemove(NoSQL noSQL) { + // Remove this role from all users, which has it + NoSQLQuery query = noSQL.createQueryBuilder() + .andCondition("roleIds", id) + .build(); + + List 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); + } + + // Remove this scope from all users, which has it + query = noSQL.createQueryBuilder() + .andCondition("scopeIds", id) + .build(); + + 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); + } + + } } diff --git a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/SocialLinkData.java b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/SocialLinkData.java index 0ed96a416e..7cfe6f5096 100644 --- a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/SocialLinkData.java +++ b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/SocialLinkData.java @@ -1,5 +1,6 @@ package org.keycloak.services.models.nosql.keycloak.data; +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; @@ -9,22 +10,12 @@ import org.keycloak.services.models.nosql.api.NoSQLObject; * @author Marek Posolda */ @NoSQLCollection(collectionName = "socialLinks") -public class SocialLinkData implements NoSQLObject { +public class SocialLinkData extends AbstractNoSQLObject { - private String id; private String socialUsername; private String socialProvider; private String userId; - @NoSQLId - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - @NoSQLField public String getSocialUsername() { return socialUsername; 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 cde0b42940..c2321fb430 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,9 +1,12 @@ package org.keycloak.services.models.nosql.keycloak.data; import org.keycloak.services.models.nosql.api.AbstractAttributedNoSQLObject; +import org.keycloak.services.models.nosql.api.NoSQL; import org.keycloak.services.models.nosql.api.NoSQLCollection; import org.keycloak.services.models.nosql.api.NoSQLField; import org.keycloak.services.models.nosql.api.NoSQLId; +import org.keycloak.services.models.nosql.api.query.NoSQLQuery; +import org.keycloak.services.models.nosql.keycloak.data.credentials.PasswordData; /** * @author Marek Posolda @@ -103,4 +106,15 @@ public class UserData extends AbstractAttributedNoSQLObject { public void setScopeIds(String[] scopeIds) { this.scopeIds = scopeIds; } + + @Override + public void afterRemove(NoSQL noSQL) { + NoSQLQuery query = noSQL.createQueryBuilder() + .andCondition("userId", id) + .build(); + + // Remove social links and passwords of this user + noSQL.removeObjects(SocialLinkData.class, query); + noSQL.removeObjects(PasswordData.class, query); + } } diff --git a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/credentials/PasswordData.java b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/credentials/PasswordData.java index a5ffad495b..4834316b22 100644 --- a/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/credentials/PasswordData.java +++ b/services/src/main/java/org/keycloak/services/models/nosql/keycloak/data/credentials/PasswordData.java @@ -2,6 +2,7 @@ package org.keycloak.services.models.nosql.keycloak.data.credentials; import java.util.Date; +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; @@ -11,9 +12,8 @@ import org.keycloak.services.models.nosql.api.NoSQLObject; * @author Marek Posolda */ @NoSQLCollection(collectionName = "passwordCredentials") -public class PasswordData implements NoSQLObject { +public class PasswordData extends AbstractNoSQLObject { - private String id; private Date effectiveDate = new Date(); private Date expiryDate; private String encodedHash; @@ -21,15 +21,6 @@ public class PasswordData implements NoSQLObject { private String userId; - @NoSQLId - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - @NoSQLField public Date getEffectiveDate() { return effectiveDate; 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 dc8d7111eb..dabba4072a 100755 --- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java +++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java @@ -62,9 +62,25 @@ public class KeycloakApplication extends Application { } public static KeycloakSessionFactory buildSessionFactory() { - // EntityManagerFactory emf = Persistence.createEntityManagerFactory("keycloak-identity-store"); - // return new PicketlinkKeycloakSessionFactory(emf, buildPartitionManager()); - return new MongoDBSessionFactory("localhost", 27017, "keycloak", true); + String sessionFactoryType = System.getProperty("keycloak.sessionFactory", "picketlink"); + if ("mongo".equals(sessionFactoryType)) { + return buildMongoDBSessionFactory(); + } else { + return buildPicketlinkSessionFactory(); + } + } + + private static KeycloakSessionFactory buildPicketlinkSessionFactory() { + EntityManagerFactory emf = Persistence.createEntityManagerFactory("keycloak-identity-store"); + return new PicketlinkKeycloakSessionFactory(emf, buildPartitionManager()); + } + + private static KeycloakSessionFactory buildMongoDBSessionFactory() { + String host = System.getProperty("keycloak.mongodb.host", "localhost"); + int port = Integer.parseInt(System.getProperty("keycloak.mongodb.port", "27017")); + String dbName = System.getProperty("keycloak.mongodb.databaseName", "keycloak"); + boolean removeAllObjectsOnStartup = Boolean.parseBoolean(System.getProperty("keycloak.mongodb.removeAllObjectsOnStartup", "true")); + return new MongoDBSessionFactory(host, port, dbName, removeAllObjectsOnStartup); } public KeycloakSessionFactory getFactory() {