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 *     dmetzler
018 */
019package org.nuxeo.ecm.core.storage.mongodb;
020
021import static java.util.concurrent.TimeUnit.SECONDS;
022
023import java.io.File;
024import java.io.FileInputStream;
025import java.io.IOException;
026import java.io.InputStream;
027
028import org.apache.commons.logging.Log;
029import org.apache.commons.logging.LogFactory;
030import org.bson.Document;
031import org.nuxeo.common.xmap.XMap;
032import org.nuxeo.launcher.config.ConfigurationException;
033import org.nuxeo.launcher.config.ConfigurationGenerator;
034import org.nuxeo.launcher.config.backingservices.BackingChecker;
035import org.nuxeo.runtime.mongodb.MongoDBConnectionConfig;
036import org.nuxeo.runtime.mongodb.MongoDBConnectionHelper;
037
038import com.mongodb.MongoTimeoutException;
039import com.mongodb.client.MongoClient;
040import com.mongodb.client.MongoDatabase;
041
042public class MongoDBChecker implements BackingChecker {
043
044    private static final Log log = LogFactory.getLog(MongoDBChecker.class);
045
046    public static final String TEMPLATE_NAME = "mongodb";
047
048    public static final String CONFIG_NAME = "mongodb-connection-config.xml";
049
050    /**
051     * @since 9.3
052     */
053    public static final String PARAM_MONGODB_CHECK_TIMEOUT = "nuxeo.mongodb.check.timeout";
054
055    /**
056     * @since 9.3
057     */
058    public static final int DEFAULT_CHECK_TIMEOUT_IN_SECONDS = 5;
059
060    @Override
061    public boolean accepts(ConfigurationGenerator cg) {
062        return cg.getTemplateList().contains(TEMPLATE_NAME);
063    }
064
065    @Override
066    public void check(ConfigurationGenerator cg) throws ConfigurationException {
067        File configFile = new File(cg.getConfigDir(), CONFIG_NAME);
068        if (!configFile.exists()) {
069            log.warn("Unable to find config file " + CONFIG_NAME);
070            return;
071        }
072        MongoDBConnectionConfig config = getDescriptor(configFile, MongoDBConnectionConfig.class);
073        try (MongoClient mongoClient = MongoDBConnectionHelper.newMongoClient(config,
074                builder -> builder.applicationName("Nuxeo DB Check")
075                                  .applyToClusterSettings(
076                                          s -> s.serverSelectionTimeout(getCheckTimeoutInSeconds(cg), SECONDS)))) {
077            MongoDatabase database = mongoClient.getDatabase(config.dbname);
078            Document ping = new Document("ping", "1");
079            database.runCommand(ping);
080        } catch (MongoTimeoutException e) {
081            throw new ConfigurationException(
082                    String.format("Unable to connect to MongoDB at %s, please check your connection", config.server),
083                    e);
084        }
085    }
086
087    /**
088     * Creates a descriptor instance for the specified file and descriptor class.
089     *
090     * @since 11.1
091     */
092    @SuppressWarnings("unchecked")
093    public <T> T getDescriptor(File file, Class<T> klass) throws ConfigurationException {
094        XMap xmap = new XMap();
095        xmap.register(klass);
096        try (InputStream inStream = new FileInputStream(file)) {
097            return (T) xmap.load(inStream);
098        } catch (IOException e) {
099            throw new ConfigurationException("Unable to load the configuration for " + klass.getSimpleName(), e);
100        }
101    }
102
103    /**
104     * Returns the value of the check timeout parameter in seconds. If value is not parseable or not set, then use the
105     * default value.
106     *
107     * @return the timeout check in seconds.
108     * @since 9.3
109     */
110    private int getCheckTimeoutInSeconds(ConfigurationGenerator cg) {
111        int checkTimeout = DEFAULT_CHECK_TIMEOUT_IN_SECONDS;
112        try {
113            checkTimeout = Integer.parseInt(
114                    cg.getUserConfig()
115                      .getProperty(PARAM_MONGODB_CHECK_TIMEOUT, String.valueOf(DEFAULT_CHECK_TIMEOUT_IN_SECONDS)));
116        } catch (NumberFormatException e) {
117            log.warn(String.format("Invalid format for %s parameter, using default value instead",
118                    PARAM_MONGODB_CHECK_TIMEOUT), e);
119        }
120        return checkTimeout;
121    }
122}