001/*
002 * (C) Copyright 2017-2018 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 *     Kevin Leturc
018 */
019package org.nuxeo.runtime.mongodb;
020
021import java.util.Collection;
022import java.util.Map;
023import java.util.concurrent.ConcurrentHashMap;
024
025import org.apache.logging.log4j.LogManager;
026import org.apache.logging.log4j.Logger;
027import org.nuxeo.runtime.model.ComponentContext;
028import org.nuxeo.runtime.model.ComponentStartOrders;
029import org.nuxeo.runtime.model.DefaultComponent;
030
031import com.mongodb.client.MongoClient;
032import com.mongodb.client.MongoDatabase;
033
034/**
035 * Component used to get a database connection to MongoDB. Don't expose {@link MongoClient} directly, because it's this
036 * component which is responsible for creating and closing it.
037 *
038 * @since 9.1
039 */
040public class MongoDBComponent extends DefaultComponent implements MongoDBConnectionService {
041
042    private static final Logger log = LogManager.getLogger(MongoDBComponent.class);
043
044    /**
045     * @since 10.3
046     */
047    public static final String COMPONENT_NAME = "org.nuxeo.runtime.mongodb.MongoDBComponent";
048
049    private static final String XP_CONNECTION = "connection";
050
051    private static final String DEFAULT_CONNECTION_ID = "default";
052
053    private final Map<String, MongoClient> clients = new ConcurrentHashMap<>();
054
055    @Override
056    public void start(ComponentContext context) {
057        super.start(context);
058        Collection<MongoDBConnectionConfig> confs = getDescriptors(XP_CONNECTION);
059        confs.forEach(c -> {
060            log.debug("Initializing MongoClient with id={}", c::getId);
061            clients.put(c.getId(), MongoDBConnectionHelper.newMongoClient(c));
062        });
063    }
064
065    @Override
066    @SuppressWarnings("Java8MapForEach")
067    public void stop(ComponentContext context) throws InterruptedException {
068        super.stop(context);
069        // don't remove entrySet otherwise java will try to load mongo client classes even in a non mongo setup
070        clients.entrySet().forEach(e -> {
071            log.debug("Closing MongoClient with id={}", e::getKey);
072            e.getValue().close();
073        });
074        clients.clear();
075    }
076
077    @Override
078    public int getApplicationStartedOrder() {
079        // start before repository and directories
080        return ComponentStartOrders.REPOSITORY - 10;
081    }
082
083    @SuppressWarnings("resource") // client closed by stop()
084    @Override
085    public MongoClient getClient(String id) {
086        MongoClient client = clients.get(id);
087        if (client == null) {
088            client = clients.get(DEFAULT_CONNECTION_ID);
089        }
090        return client;
091    }
092
093    @Override
094    public MongoDBConnectionConfig getConfig(String id) {
095        MongoDBConnectionConfig config = getDescriptor(XP_CONNECTION, id);
096        if (config == null) {
097            config = getDescriptor(XP_CONNECTION, DEFAULT_CONNECTION_ID);
098        }
099        return config;
100    }
101
102    @Override
103    public String getDatabaseName(String id) {
104        return getConfig(id).dbname;
105    }
106
107    @SuppressWarnings("resource") // client is closed only at stop() time
108    @Override
109    public MongoDatabase getDatabase(String id) {
110        MongoClient client = getClient(id);
111        String dbname = getDatabaseName(id);
112        return MongoDBConnectionHelper.getDatabase(client, dbname);
113    }
114
115    @Override
116    public Iterable<MongoDatabase> getDatabases() {
117        return () -> clients.entrySet().stream().map(e -> {
118            MongoDBConnectionConfig c = getDescriptor(XP_CONNECTION, e.getKey());
119            return MongoDBConnectionHelper.getDatabase(e.getValue(), c.dbname);
120        }).iterator();
121    }
122
123}