package org.ff4j.mongo.store;
import static org.ff4j.mongo.MongoDbConstants.MONGO_SET;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import org.bson.Document;
import org.ff4j.core.Feature;
import org.ff4j.core.FeatureStore;
import org.ff4j.exception.FeatureAlreadyExistException;
import org.ff4j.exception.FeatureNotFoundException;
import org.ff4j.exception.GroupNotFoundException;
import org.ff4j.mongo.MongoDbConstants;
import org.ff4j.mongo.mapper.FeatureDocumentBuilder;
import org.ff4j.mongo.mapper.MongoFeatureMapper;
import org.ff4j.store.AbstractFeatureStore;
import org.ff4j.utils.Util;
import com.mongodb.MongoClient;
/*
* #%L ff4j-store-jdbc %% Copyright (C) 2013 Ff4J %% Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License. #L%
*/
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
/**
* Implementation of {@link FeatureStore} to work with MongoDB.
*
* @author William Delanoue (@twillouer) </a>
* @author Cedrick Lunven (@clunven)</a>
*/
public class FeatureStoreMongo extends AbstractFeatureStore {
/** Map from DBObject to Feature. */
private static final MongoFeatureMapper FMAPPER = new MongoFeatureMapper();
/** Build fields. */
private static final FeatureDocumentBuilder BUILDER = new FeatureDocumentBuilder();
/** error message. */
public static final String FEATURE_IDENTIFIER_CANNOT_BE_NULL_NOR_EMPTY = "Feature identifier cannot be null nor empty";
/** error message. */
public static final String GROUPNAME_CANNOT_BE_NULL_NOR_EMPTY = "Groupname cannot be null nor empty";
/** MongoDB collection. */
private MongoCollection<Document> featuresCollection;
/** Feature collection Name. */
private String collectionName = MongoDbConstants.DEFAULT_FEATURE_COLLECTION;
/** Database name. */
private String dbName = MongoDbConstants.DEFAULT_DBNAME;
/** Current mongo client. */
private MongoClient mongoClient;
/**
* Parameterized constructor with collection.
*
* @param collection
* the collection to set
*/
public FeatureStoreMongo() {
}
/**
* Parameterized constructor with collection.
*
* @param collection
* the collection to set
*/
public FeatureStoreMongo(MongoClient client) {
this.mongoClient = client;
this.featuresCollection = getFeaturesCollection();
}
/**
* Parameterized constructor with collection.
*
* @param collection
* the collection to set
*/
public FeatureStoreMongo(MongoClient client, String dbName, String collectionName) {
this.mongoClient = client;
this.collectionName = collectionName;
this.dbName = dbName;
this.featuresCollection = getFeaturesCollection();
}
/**
* Parameterized constructor with collection.
*
* @param collection
* the collection to set
*/
public FeatureStoreMongo(MongoDatabase db) {
this(db, MongoDbConstants.DEFAULT_FEATURE_COLLECTION);
}
/**
* Parameterized constructor with collection.
*
* @param collection
* the collection to set
*/
public FeatureStoreMongo(MongoDatabase db, String collectionName) {
this.featuresCollection = db.getCollection(collectionName);
this.dbName = db.getName();
}
/**
* Parameterized constructor with collection.
*
* @param collection
* the collection to set
*/
public FeatureStoreMongo(MongoCollection<Document> collection) {
this.featuresCollection = collection;
}
/**
* Parameterized constructor with collection.
*
* @param collection
* the collection to set
*/
public FeatureStoreMongo(MongoCollection<Document> collection, String xmlConfFile) {
this(collection);
importFeaturesFromXmlFile(xmlConfFile);
}
/** {@inheritDoc} */
@Override
public void enable(String featId) {
this.updateStatus(featId, true);
}
/** {@inheritDoc} */
@Override
public void disable(String featId) {
this.updateStatus(featId, false);
}
/**
* Update status of feature.
*
* @param uid
* feature id
* @param enable
* enabler
*/
private void updateStatus(String uid, boolean enable) {
if (uid == null || uid.isEmpty()) {
throw new IllegalArgumentException(FEATURE_IDENTIFIER_CANNOT_BE_NULL_NOR_EMPTY);
}
if (!exist(uid)) {
throw new FeatureNotFoundException(uid);
}
Document target = BUILDER.getFeatUid(uid);
Object enabledd = BUILDER.getEnable(enable);
getFeaturesCollection().updateOne(target, new Document(MONGO_SET, enabledd));
}
/** {@inheritDoc} */
@Override
public boolean exist(String featId) {
Util.assertHasLength(featId);
return 1 == getFeaturesCollection().count(BUILDER.getFeatUid(featId));
}
/** {@inheritDoc} */
@Override
public Feature read(String uid) {
if (uid == null || uid.isEmpty()) {
throw new IllegalArgumentException(FEATURE_IDENTIFIER_CANNOT_BE_NULL_NOR_EMPTY);
}
Document object = getFeaturesCollection().find(BUILDER.getFeatUid(uid)).first();
if (object==null) {
throw new FeatureNotFoundException(uid);
}
return FMAPPER.fromStore(object);
}
/** {@inheritDoc} */
@Override
public void create(Feature fp) {
if (fp == null) {
throw new IllegalArgumentException("Feature cannot be null nor empty");
}
if (exist(fp.getUid())) {
throw new FeatureAlreadyExistException(fp.getUid());
}
getFeaturesCollection().insertOne(FMAPPER.toStore(fp));
}
/** {@inheritDoc} */
@Override
public void delete(String uid) {
if (uid == null || uid.isEmpty()) {
throw new IllegalArgumentException(FEATURE_IDENTIFIER_CANNOT_BE_NULL_NOR_EMPTY);
}
if (!exist(uid)) {
throw new FeatureNotFoundException(uid);
}
getFeaturesCollection().deleteOne(BUILDER.getFeatUid(uid));
}
/** {@inheritDoc} */
@Override
public void grantRoleOnFeature(String uid, String roleName) {
if (uid == null || uid.isEmpty()) {
throw new IllegalArgumentException(FEATURE_IDENTIFIER_CANNOT_BE_NULL_NOR_EMPTY);
}
if (roleName == null || roleName.isEmpty()) {
throw new IllegalArgumentException("roleName cannot be null nor empty");
}
if (!exist(uid)) {
throw new FeatureNotFoundException(uid);
}
getFeaturesCollection().updateOne(BUILDER.getFeatUid(uid), new Document("$addToSet", BUILDER.getRoles(roleName)));
}
/** {@inheritDoc} */
@Override
public void removeRoleFromFeature(String uid, String roleName) {
if (uid == null || uid.isEmpty()) {
throw new IllegalArgumentException(FEATURE_IDENTIFIER_CANNOT_BE_NULL_NOR_EMPTY);
}
if (roleName == null || roleName.isEmpty()) {
throw new IllegalArgumentException("roleName cannot be null nor empty");
}
if (!exist(uid)) {
throw new FeatureNotFoundException(uid);
}
getFeaturesCollection().updateOne(BUILDER.getFeatUid(uid), new Document("$pull", BUILDER.getRoles(roleName)));
}
/** {@inheritDoc} */
@Override
public Map<String, Feature> readAll() {
LinkedHashMap<String, Feature> mapFP = new LinkedHashMap<String, Feature>();
for(Document document : getFeaturesCollection().find()) {
Feature feature = FMAPPER.fromStore(document);
mapFP.put(feature.getUid(), feature);
}
return mapFP;
}
/** {@inheritDoc} */
@Override
public void update(Feature fp) {
if (fp == null) {
throw new IllegalArgumentException("Feature cannot be null nor empty");
}
read(fp.getUid());
getFeaturesCollection().updateOne(BUILDER.getFeatUid(fp.getUid()), new Document(MONGO_SET, FMAPPER.toStore(fp)));
}
/** {@inheritDoc} */
@Override
public boolean existGroup(String groupName) {
if (groupName == null || groupName.isEmpty()) {
throw new IllegalArgumentException(GROUPNAME_CANNOT_BE_NULL_NOR_EMPTY);
}
return getFeaturesCollection().count(BUILDER.getGroupName(groupName)) > 0;
}
/** {@inheritDoc} */
@Override
public Set<String> readAllGroups() {
Set<String> setOfGroups = new HashSet<String>();
for (Document document : getFeaturesCollection().find()) {
setOfGroups.add(document.getString(MongoDbConstants.FEATURE_GROUPNAME));
}
setOfGroups.remove(null);
setOfGroups.remove("");
return setOfGroups;
}
/** {@inheritDoc} */
@Override
public Map<String, Feature> readGroup(String groupName) {
if (groupName == null || groupName.isEmpty()) {
throw new IllegalArgumentException(GROUPNAME_CANNOT_BE_NULL_NOR_EMPTY);
}
if (!existGroup(groupName)) {
throw new GroupNotFoundException(groupName);
}
LinkedHashMap<String, Feature> mapFP = new LinkedHashMap<String, Feature>();
for (Document document : getFeaturesCollection().find(BUILDER.getGroupName(groupName))) {
Feature feature = FMAPPER.fromStore(document);
mapFP.put(feature.getUid(), feature);
}
return mapFP;
}
/** {@inheritDoc} */
@Override
public void enableGroup(String groupName) {
if (groupName == null || groupName.isEmpty()) {
throw new IllegalArgumentException(GROUPNAME_CANNOT_BE_NULL_NOR_EMPTY);
}
if (!existGroup(groupName)) {
throw new GroupNotFoundException(groupName);
}
for (Document document : getFeaturesCollection().find(BUILDER.getGroupName(groupName))) {
Object enabled = BUILDER.getEnable(true);
getFeaturesCollection().updateOne(document, new Document(MONGO_SET, enabled));
}
}
/** {@inheritDoc} */
@Override
public void disableGroup(String groupName) {
if (groupName == null || groupName.isEmpty()) {
throw new IllegalArgumentException(GROUPNAME_CANNOT_BE_NULL_NOR_EMPTY);
}
if (!existGroup(groupName)) {
throw new GroupNotFoundException(groupName);
}
for (Document document: getFeaturesCollection().find(BUILDER.getGroupName(groupName))) {
Object enabled = BUILDER.getEnable(false);
getFeaturesCollection().updateOne(document, new Document(MONGO_SET, enabled));
}
}
/** {@inheritDoc} */
@Override
public void addToGroup(String uid, String groupName) {
if (uid == null || uid.isEmpty()) {
throw new IllegalArgumentException(FEATURE_IDENTIFIER_CANNOT_BE_NULL_NOR_EMPTY);
}
if (groupName == null || groupName.isEmpty()) {
throw new IllegalArgumentException(GROUPNAME_CANNOT_BE_NULL_NOR_EMPTY);
}
if (!exist(uid)) {
throw new FeatureNotFoundException(uid);
}
Document target = BUILDER.getFeatUid(uid);
Document nGroupName = BUILDER.getGroupName(groupName);
getFeaturesCollection().updateOne(target, new Document(MONGO_SET, nGroupName));
}
/** {@inheritDoc} */
@Override
public void removeFromGroup(String uid, String groupName) {
if (uid == null || uid.isEmpty()) {
throw new IllegalArgumentException(FEATURE_IDENTIFIER_CANNOT_BE_NULL_NOR_EMPTY);
}
if (groupName == null || groupName.isEmpty()) {
throw new IllegalArgumentException(GROUPNAME_CANNOT_BE_NULL_NOR_EMPTY);
}
if (!exist(uid)) {
throw new FeatureNotFoundException(uid);
}
if (!existGroup(groupName)) {
throw new GroupNotFoundException(groupName);
}
Document target = BUILDER.getFeatUid(uid);
Document nGroupName = BUILDER.getGroupName("");
getFeaturesCollection().updateOne(target, new Document(MONGO_SET, nGroupName));
}
/** {@inheritDoc} */
@Override
public void clear() {
getFeaturesCollection().deleteMany(new Document());
}
/** {@inheritDoc} */
@Override
public void createSchema() {
if (!mongoClient.getDatabase(dbName)
.listCollectionNames()
.into(new HashSet<String>())
.contains(collectionName)) {
mongoClient.getDatabase(dbName).createCollection(collectionName);
}
featuresCollection = mongoClient.getDatabase(dbName).getCollection(collectionName);
}
/**
* Getter accessor for attribute 'featuresCollection'.
*
* @return
* current value of 'featuresCollection'
*/
public MongoCollection<Document> getFeaturesCollection() {
if (featuresCollection == null) {
if (mongoClient != null) {
createSchema();
} else {
throw new IllegalStateException("Cannot initialize Features collection : no mongo client defined");
}
}
return featuresCollection;
}
}