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