001/*
002 * (C) Copyright 2017 Nuxeo (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Funsho David
018 *
019 */
020
021package org.nuxeo.directory.mongodb;
022
023import static org.nuxeo.ecm.directory.BaseDirectoryDescriptor.CREATE_TABLE_POLICY_ALWAYS;
024import static org.nuxeo.ecm.directory.BaseDirectoryDescriptor.CREATE_TABLE_POLICY_ON_MISSING_COLUMNS;
025import static org.nuxeo.directory.mongodb.MongoDBSerializationHelper.MONGODB_ID;
026import static org.nuxeo.directory.mongodb.MongoDBSerializationHelper.MONGODB_SEQ;
027
028import java.util.Arrays;
029import java.util.HashMap;
030import java.util.LinkedHashMap;
031import java.util.Map;
032
033import org.nuxeo.ecm.core.cache.CacheService;
034import org.nuxeo.ecm.core.schema.SchemaManager;
035import org.nuxeo.ecm.core.schema.types.Schema;
036import org.nuxeo.ecm.directory.AbstractDirectory;
037import org.nuxeo.ecm.directory.Directory;
038import org.nuxeo.ecm.directory.DirectoryCSVLoader;
039import org.nuxeo.ecm.directory.DirectoryException;
040import org.nuxeo.ecm.directory.Session;
041import org.nuxeo.runtime.api.Framework;
042
043import com.mongodb.client.MongoCollection;
044import com.mongodb.client.model.Filters;
045
046/**
047 * MongoDB implementation of a {@link Directory}
048 * 
049 * @since 9.1
050 */
051public class MongoDBDirectory extends AbstractDirectory {
052
053    protected String countersCollectionName;
054
055    protected boolean initialized;
056
057    public MongoDBDirectory(MongoDBDirectoryDescriptor descriptor) {
058        super(descriptor, MongoDBReference.class);
059
060        // Add specific references
061        addMongoDBReferences(descriptor.getMongoDBReferences());
062
063        // cache fallback
064        CacheService cacheService = Framework.getService(CacheService.class);
065        if (cacheService != null) {
066            if (descriptor.cacheEntryName == null && descriptor.getCacheMaxSize() != 0) {
067                cache.setEntryCacheName("cache-" + getName());
068                cacheService.registerCache("cache-" + getName(), descriptor.getCacheMaxSize(),
069                        descriptor.getCacheTimeout() / 60);
070            }
071            if (descriptor.cacheEntryWithoutReferencesName == null && descriptor.getCacheMaxSize() != 0) {
072                cache.setEntryCacheWithoutReferencesName("cacheWithoutReference-" + getName());
073                cacheService.registerCache("cacheWithoutReference-" + getName(), descriptor.getCacheMaxSize(),
074                        descriptor.getCacheTimeout() / 60);
075            }
076        }
077
078        countersCollectionName = getName() + ".counters";
079
080    }
081
082    @Override
083    public MongoDBDirectoryDescriptor getDescriptor() {
084        return (MongoDBDirectoryDescriptor) descriptor;
085    }
086
087    @Override
088    public Session getSession() throws DirectoryException {
089
090        SchemaManager schemaManager = Framework.getService(SchemaManager.class);
091        Schema schema = schemaManager.getSchema(getSchema());
092        if (schema == null) {
093            throw new DirectoryException(getSchema() + " is not a registered schema");
094        }
095        schemaFieldMap = new LinkedHashMap<>();
096        schema.getFields().forEach(f -> schemaFieldMap.put(f.getName().getLocalName(), f));
097
098        MongoDBSession session = new MongoDBSession(this);
099        addSession(session);
100
101        // Initialize counters collection if autoincrement enabled
102        if (descriptor.isAutoincrementIdField() && !session.hasCollection(countersCollectionName)) {
103            Map<String, Object> seq = new HashMap<>();
104            seq.put(MONGODB_ID, getName());
105            seq.put(MONGODB_SEQ, 0L);
106            session.getCollection(countersCollectionName).insertOne(MongoDBSerializationHelper.fieldMapToBson(seq));
107        }
108
109        if (!initialized) {
110            String policy = descriptor.getCreateTablePolicy();
111            MongoCollection collection = session.getCollection(getName());
112            boolean dropCollection = false;
113            boolean loadData = false;
114
115            switch (policy) {
116            case CREATE_TABLE_POLICY_ALWAYS:
117                dropCollection = true;
118                loadData = true;
119                break;
120            case CREATE_TABLE_POLICY_ON_MISSING_COLUMNS:
121                // As MongoDB does not have the notion of columns, only load data if collection doesn't exist
122                if (!session.hasCollection(getName())) {
123                    loadData = true;
124                }
125            default:
126                break;
127            }
128            if (dropCollection) {
129                collection.drop();
130            }
131            if (loadData) {
132                loadData(schema, session);
133            }
134            initialized = true;
135        }
136        return session;
137    }
138
139    protected void loadData(Schema schema, Session session) {
140        if (descriptor.getDataFileName() != null) {
141            Framework.doPrivileged(() -> DirectoryCSVLoader.loadData(descriptor.getDataFileName(),
142                    descriptor.getDataFileCharacterSeparator(), schema, session::createEntry));
143        }
144    }
145
146    protected void addMongoDBReferences(MongoDBReferenceDescriptor[] mongodbReferences) {
147        if (mongodbReferences != null) {
148            Arrays.stream(mongodbReferences).map(MongoDBReference::new).forEach(this::addReference);
149        }
150    }
151
152    public String getCountersCollectionName() {
153        return countersCollectionName;
154    }
155}