001/*
002 * (C) Copyright 2017 Nuxeo SA (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.Iterator;
022import java.util.Map;
023import java.util.Map.Entry;
024import java.util.concurrent.ConcurrentHashMap;
025
026import org.apache.commons.logging.Log;
027import org.apache.commons.logging.LogFactory;
028import org.nuxeo.runtime.model.ComponentContext;
029import org.nuxeo.runtime.model.ComponentInstance;
030import org.nuxeo.runtime.model.ComponentStartOrders;
031import org.nuxeo.runtime.model.ContributionFragmentRegistry.FragmentList;
032import org.nuxeo.runtime.model.DefaultComponent;
033import org.nuxeo.runtime.model.SimpleContributionRegistry;
034
035import com.mongodb.MongoClient;
036import com.mongodb.client.MongoDatabase;
037
038/**
039 * Component used to get a database connection to MongoDB. Don't expose {@link MongoClient} directly, because it's this
040 * component which is responsible for creating and closing it.
041 *
042 * @since 9.1
043 */
044public class MongoDBComponent extends DefaultComponent implements MongoDBConnectionService {
045
046    private static final Log log = LogFactory.getLog(MongoDBComponent.class);
047
048    public static final String NAME = "org.nuxeo.runtime.mongodb.MongoDBComponent";
049
050    private static final String EP_CONNECTION = "connection";
051
052    private static final String DEFAULT_CONNECTION_ID = "default";
053
054    private final MongoDBConnectionConfigRegistry registry = new MongoDBConnectionConfigRegistry();
055
056    private final Map<String, MongoClient> clients = new ConcurrentHashMap<>();
057
058    @Override
059    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
060        switch (extensionPoint) {
061        case EP_CONNECTION:
062            registry.addContribution((MongoDBConnectionConfig) contribution);
063            log.info(
064                    "Registering connection configuration: " + contribution + ", loaded from " + contributor.getName());
065            break;
066        default:
067            throw new IllegalStateException("Invalid EP: " + extensionPoint);
068        }
069    }
070
071    @Override
072    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
073        switch (extensionPoint) {
074        case EP_CONNECTION:
075            MongoDBConnectionConfig config = (MongoDBConnectionConfig) contribution;
076            log.info("Unregistering connection configuration: " + config);
077            clients.remove(config.getId()).close();
078            registry.removeContribution(config);
079            break;
080        default:
081            throw new IllegalStateException("Invalid EP: " + extensionPoint);
082        }
083    }
084
085    @Override
086    public void start(ComponentContext context) {
087        log.info("Activate MongoDB component");
088        for (FragmentList<MongoDBConnectionConfig> fragment : registry.getFragments()) {
089            MongoDBConnectionConfig conf = fragment.object;
090            log.debug("Initializing MongoClient with id=" + conf.getId());
091            @SuppressWarnings("resource")
092            MongoClient client = MongoDBConnectionHelper.newMongoClient(conf.getServer());
093            clients.put(conf.getId(), client);
094        }
095    }
096
097    @Override
098    public void stop(ComponentContext context) {
099        Iterator<Entry<String, MongoClient>> it = clients.entrySet().iterator();
100        while (it.hasNext()) {
101            Entry<String, MongoClient> entry = it.next();
102            log.debug("Closing MongoClient with id=" + entry.getKey());
103            MongoClient client = entry.getValue();
104            client.close();
105            it.remove();
106        }
107    }
108
109    @Override
110    public int getApplicationStartedOrder() {
111        // start before repository
112        return ComponentStartOrders.REPOSITORY - 1;
113    }
114
115    /**
116     * @param id the connection id to retrieve.
117     * @return the database configured by {@link MongoDBConnectionConfig} for the input id, or the default one if it
118     *         doesn't exist
119     */
120    @Override
121    public MongoDatabase getDatabase(String id) {
122        MongoDBConnectionConfig config = registry.getCurrentContribution(id);
123        MongoClient client = clients.get(id);
124        if (client == null) {
125            config = registry.getCurrentContribution(DEFAULT_CONNECTION_ID);
126            client = clients.get(DEFAULT_CONNECTION_ID);
127        }
128        return MongoDBConnectionHelper.getDatabase(client, config.getDbname());
129    }
130
131    /**
132     * @return all configured databases
133     */
134    @Override
135    public Iterable<MongoDatabase> getDatabases() {
136        return () -> clients.entrySet()
137                            .stream()
138                            .map(e -> MongoDBConnectionHelper.getDatabase(e.getValue(),
139                                    registry.getCurrentContribution(e.getKey()).getDbname()))
140                            .iterator();
141    }
142
143    protected static class MongoDBConnectionConfigRegistry extends SimpleContributionRegistry<MongoDBConnectionConfig> {
144
145        @Override
146        public String getContributionId(MongoDBConnectionConfig contrib) {
147            return contrib.getId();
148        }
149
150        @Override
151        public MongoDBConnectionConfig getCurrentContribution(String id) {
152            return super.getCurrentContribution(id);
153        }
154
155    }
156
157}