org.opencb.opencga.storage.mongodb.metadata.MongoDBStudyConfigurationManager.java Source code

Java tutorial

Introduction

Here is the source code for org.opencb.opencga.storage.mongodb.metadata.MongoDBStudyConfigurationManager.java

Source

/*
 * Copyright 2015-2016 OpenCB
 *
 * 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.
 */

package org.opencb.opencga.storage.mongodb.metadata;

import com.mongodb.DuplicateKeyException;
import com.mongodb.MongoWriteException;
import com.mongodb.ReadPreference;
import com.mongodb.WriteConcern;
import com.mongodb.client.model.Projections;
import com.mongodb.client.model.Updates;
import com.mongodb.client.result.UpdateResult;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.opencb.commons.datastore.core.QueryOptions;
import org.opencb.commons.datastore.core.QueryResult;
import org.opencb.commons.datastore.mongodb.MongoDBCollection;
import org.opencb.commons.datastore.mongodb.MongoDataStore;
import org.opencb.commons.datastore.mongodb.MongoDataStoreManager;
import org.opencb.opencga.storage.core.metadata.StudyConfiguration;
import org.opencb.opencga.storage.core.metadata.StudyConfigurationManager;
import org.opencb.opencga.storage.mongodb.auth.MongoCredentials;
import org.opencb.opencga.storage.mongodb.utils.MongoLock;
import org.opencb.opencga.storage.mongodb.variant.converters.DocumentToStudyConfigurationConverter;

import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;

import static com.mongodb.client.model.Updates.set;
import static org.opencb.commons.datastore.mongodb.MongoDBCollection.UPSERT;

/**
 * @author Jacobo Coll <jacobo167@gmail.com>
 */
public class MongoDBStudyConfigurationManager extends StudyConfigurationManager {

    private final MongoDataStoreManager mongoManager;
    private final boolean closeConnection;

    private final DocumentToStudyConfigurationConverter studyConfigurationConverter = new DocumentToStudyConfigurationConverter();
    private final MongoLock mongoLock;
    private final MongoDBCollection collection;

    public MongoDBStudyConfigurationManager(MongoCredentials credentials, String collectionName)
            throws UnknownHostException {
        this(new MongoDataStoreManager(credentials.getDataStoreServerAddresses()), true, credentials,
                collectionName);
    }

    public MongoDBStudyConfigurationManager(MongoDataStoreManager mongoManager, MongoCredentials credentials,
            String collectionName) throws UnknownHostException {
        this(mongoManager, false, credentials, collectionName);
    }

    private MongoDBStudyConfigurationManager(MongoDataStoreManager mongoManager, boolean closeConnection,
            MongoCredentials credentials, String collectionName) throws UnknownHostException {
        super(null);
        // Mongo configuration
        this.mongoManager = mongoManager;
        this.closeConnection = closeConnection;
        MongoDataStore db = mongoManager.get(credentials.getMongoDbName(), credentials.getMongoDBConfiguration());
        collection = db.getCollection(collectionName).withReadPreference(ReadPreference.primary())
                .withWriteConcern(WriteConcern.ACKNOWLEDGED);
        mongoLock = new MongoLock(collection, "_lock");
    }

    @Override
    protected QueryResult<StudyConfiguration> internalGetStudyConfiguration(String studyName, Long timeStamp,
            QueryOptions options) {
        return internalGetStudyConfiguration(null, studyName, timeStamp, options);
    }

    @Override
    protected QueryResult<StudyConfiguration> internalGetStudyConfiguration(int studyId, Long timeStamp,
            QueryOptions options) {
        return internalGetStudyConfiguration(studyId, null, timeStamp, options);
    }

    @Override
    public long lockStudy(int studyId, long lockDuration, long timeout)
            throws InterruptedException, TimeoutException {
        try {
            // Ensure document exists
            collection.update(new Document("_id", studyId), set("id", studyId),
                    new QueryOptions(MongoDBCollection.UPSERT, true));
        } catch (MongoWriteException e) {
            // Duplicated key exception
            if (e.getError().getCode() != 11000) {
                throw e;
            }
        } catch (DuplicateKeyException ignore) {
            // Ignore this exception.
            // With UPSERT=true, this command should never throw DuplicatedKeyException.
            // See https://jira.mongodb.org/browse/SERVER-14322
        }
        return mongoLock.lock(studyId, lockDuration, timeout);
    }

    @Override
    public void unLockStudy(int studyId, long lockId) {
        mongoLock.unlock(studyId, lockId);
    }

    private QueryResult<StudyConfiguration> internalGetStudyConfiguration(Integer studyId, String studyName,
            Long timeStamp, QueryOptions options) {
        long start = System.currentTimeMillis();
        StudyConfiguration studyConfiguration;

        Document query = new Document();
        if (studyId != null) {
            query.append("_id", studyId);
        }
        if (studyName != null) {
            query.append("studyName", studyName);
        }
        if (timeStamp != null) {
            query.append("timeStamp", new Document("$ne", timeStamp));
        }

        QueryResult<StudyConfiguration> queryResult = collection.find(query, null, studyConfigurationConverter,
                null);
        if (queryResult.getResult().isEmpty()) {
            studyConfiguration = null;
        } else {
            if (queryResult.first().getStudyName() == null) {
                // If the studyName is null, it may be only a lock instead of a real study configuration
                studyConfiguration = null;
            } else {
                studyConfiguration = queryResult.first();
            }
        }

        if (studyConfiguration == null) {
            return new QueryResult<>(studyName, ((int) (System.currentTimeMillis() - start)), 0, 0, "", "",
                    Collections.emptyList());
        } else {
            return new QueryResult<>(studyName, ((int) (System.currentTimeMillis() - start)), 1, 1, "", "",
                    Collections.singletonList(studyConfiguration));
        }
    }

    @Override
    public QueryResult internalUpdateStudyConfiguration(StudyConfiguration studyConfiguration,
            QueryOptions options) {
        Document studyMongo = new DocumentToStudyConfigurationConverter().convertToStorageType(studyConfiguration);

        // Update field by field, instead of replacing the whole object to preserve existing fields like "_lock"
        Document query = new Document("_id", studyConfiguration.getStudyId());
        List<Bson> updates = new ArrayList<>(studyMongo.size());
        studyMongo.forEach((s, o) -> updates.add(new Document("$set", new Document(s, o))));
        QueryResult<UpdateResult> queryResult = collection.update(query, Updates.combine(updates),
                new QueryOptions(UPSERT, true));
        //        studyConfigurationMap.put(studyConfiguration.getStudyId(), studyConfiguration);

        return queryResult;
    }

    @Override
    public List<String> getStudyNames(QueryOptions options) {
        List<String> studyNames = collection.distinct("studyName", null).getResult();

        return studyNames.stream().map(Object::toString).collect(Collectors.toList());
    }

    @Override
    public List<Integer> getStudyIds(QueryOptions options) {
        return collection.distinct("_id", null, Integer.class).getResult();
    }

    @Override
    public Map<String, Integer> getStudies(QueryOptions options) {
        QueryResult<StudyConfiguration> queryResult = collection.find(new Document(),
                Projections.include("studyId", "studyName"), studyConfigurationConverter, null);
        return queryResult.getResult().stream()
                .collect(Collectors.toMap(StudyConfiguration::getStudyName, StudyConfiguration::getStudyId));
    }

    @Override
    public void close() {
        if (closeConnection) {
            mongoManager.close();
        }
    }
}