001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.appender.db.nosql.mongodb;
018
019import org.apache.logging.log4j.Level;
020import org.apache.logging.log4j.Logger;
021import org.apache.logging.log4j.core.appender.AppenderLoggingException;
022import org.apache.logging.log4j.core.appender.db.nosql.NoSQLConnection;
023import org.apache.logging.log4j.core.appender.db.nosql.NoSQLObject;
024import org.apache.logging.log4j.core.helpers.Strings;
025import org.apache.logging.log4j.status.StatusLogger;
026import org.bson.BSON;
027import org.bson.Transformer;
028
029import com.mongodb.BasicDBObject;
030import com.mongodb.DB;
031import com.mongodb.DBCollection;
032import com.mongodb.Mongo;
033import com.mongodb.MongoException;
034import com.mongodb.WriteConcern;
035import com.mongodb.WriteResult;
036
037/**
038 * The MongoDB implementation of {@link NoSQLConnection}.
039 */
040public final class MongoDBConnection implements NoSQLConnection<BasicDBObject, MongoDBObject> {
041
042    private static final Logger LOGGER = StatusLogger.getLogger();
043
044    static {
045        BSON.addEncodingHook(Level.class, new Transformer() {
046            @Override
047            public Object transform(Object o) {
048                if (o instanceof Level) {
049                    return ((Level) o).name();
050                }
051                return o;
052            }
053        });
054    }
055
056    private final DBCollection collection;
057    private final Mongo mongo;
058    private final WriteConcern writeConcern;
059
060    public MongoDBConnection(final DB database, final WriteConcern writeConcern, final String collectionName) {
061        this.mongo = database.getMongo();
062        this.collection = database.getCollection(collectionName);
063        this.writeConcern = writeConcern;
064    }
065
066    @Override
067    public MongoDBObject createObject() {
068        return new MongoDBObject();
069    }
070
071    @Override
072    public MongoDBObject[] createList(final int length) {
073        return new MongoDBObject[length];
074    }
075
076    @Override
077    public void insertObject(final NoSQLObject<BasicDBObject> object) {
078        try {
079            final WriteResult result = this.collection.insert(object.unwrap(), this.writeConcern);
080            if (Strings.isNotEmpty(result.getError())) {
081                throw new AppenderLoggingException("Failed to write log event to MongoDB due to error: " +
082                        result.getError() + ".");
083            }
084        } catch (final MongoException e) {
085            throw new AppenderLoggingException("Failed to write log event to MongoDB due to error: " + e.getMessage(),
086                    e);
087        }
088    }
089
090    @Override
091    public void close() {
092        this.mongo.close();
093    }
094
095    @Override
096    public boolean isClosed() {
097        return !this.mongo.getConnector().isOpen();
098    }
099
100    /**
101     * To prevent class loading issues during plugin discovery, this code cannot live within MongoDBProvider. This
102     * is because of how Java treats references to Exception classes different from references to other classes. When
103     * Java loads a class, it normally won't load that class's dependent classes until and unless A) they are used, B)
104     * the class being loaded extends or implements those classes, or C) those classes are the types of static members
105     * in the class. However, exceptions that a class uses are always loaded when the class is loaded, even before
106     * they are actually used.
107     *
108     * @param database The database to authenticate
109     * @param username The username to authenticate with
110     * @param password The password to authenticate with
111     */
112    static void authenticate(final DB database, final String username, final String password) {
113        try {
114            if (!database.authenticate(username, password.toCharArray())) {
115                LOGGER.error("Failed to authenticate against MongoDB server. Unknown error.");
116            }
117        } catch (final MongoException e) {
118            LOGGER.error("Failed to authenticate against MongoDB: " + e.getMessage(), e);
119        } catch (final IllegalStateException e) {
120            LOGGER.error("Factory-supplied MongoDB database connection already authenticated with different" +
121                    "credentials but lost connection.");
122        }
123    }
124}