View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements. See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache license, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License. You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the license for the specific language governing permissions and
    15   * limitations under the license.
    16   */
    17  package org.apache.logging.log4j.nosql.appender.mongodb;
    18
    19  import org.apache.logging.log4j.Level;
    20  import org.apache.logging.log4j.Logger;
    21  import org.apache.logging.log4j.core.appender.AppenderLoggingException;
    22  import org.apache.logging.log4j.nosql.appender.NoSqlConnection;
    23  import org.apache.logging.log4j.nosql.appender.NoSqlObject;
    24  import org.apache.logging.log4j.status.StatusLogger;
    25  import org.apache.logging.log4j.util.Strings;
    26  import org.bson.BSON;
    27  import org.bson.Transformer;
    28
    29  import com.mongodb.BasicDBObject;
    30  import com.mongodb.DB;
    31  import com.mongodb.DBCollection;
    32  import com.mongodb.Mongo;
    33  import com.mongodb.MongoException;
    34  import com.mongodb.WriteConcern;
    35  import com.mongodb.WriteResult;
    36
    37  /**
    38   * The MongoDB implementation of {@link NoSqlConnection}.
    39   */
    40  public final class MongoDbConnection implements NoSqlConnection<BasicDBObject, MongoDbObject> {
    41
    42      private static final Logger LOGGER = StatusLogger.getLogger();
    43
    44      static {
    45          BSON.addEncodingHook(Level.class, new Transformer() {
    46              @Override
    47              public Object transform(final Object o) {
    48                  if (o instanceof Level) {
    49                      return ((Level) o).name();
    50                  }
    51                  return o;
    52              }
    53          });
    54      }
    55
    56      private final DBCollection collection;
    57      private final Mongo mongo;
    58      private final WriteConcern writeConcern;
    59
    60      public MongoDbConnection(final DB database, final WriteConcern writeConcern, final String collectionName) {
    61          this.mongo = database.getMongo();
    62          this.collection = database.getCollection(collectionName);
    63          this.writeConcern = writeConcern;
    64      }
    65
    66      @Override
    67      public MongoDbObject createObject() {
    68          return new MongoDbObject();
    69      }
    70
    71      @Override
    72      public MongoDbObject[] createList(final int length) {
    73          return new MongoDbObject[length];
    74      }
    75
    76      @Override
    77      public void insertObject(final NoSqlObject<BasicDBObject> object) {
    78          try {
    79              final WriteResult result = this.collection.insert(object.unwrap(), this.writeConcern);
    80              if (Strings.isNotEmpty(result.getError())) {
    81                  throw new AppenderLoggingException("Failed to write log event to MongoDB due to error: " +
    82                          result.getError() + '.');
    83              }
    84          } catch (final MongoException e) {
    85              throw new AppenderLoggingException("Failed to write log event to MongoDB due to error: " + e.getMessage(),
    86                      e);
    87          }
    88      }
    89
    90      @Override
    91      public void close() {
    92          // there's no need to call this.mongo.close() since that literally closes the connection
    93          // MongoDBClient uses internal connection pooling
    94          // for more details, see LOG4J2-591
    95      }
    96
    97      @Override
    98      public boolean isClosed() {
    99          return !this.mongo.getConnector().isOpen();
    100     }
    101
    102     /**
    103      * To prevent class loading issues during plugin discovery, this code cannot live within MongoDbProvider. This
    104      * is because of how Java treats references to Exception classes different from references to other classes. When
    105      * Java loads a class, it normally won't load that class's dependent classes until and unless A) they are used, B)
    106      * the class being loaded extends or implements those classes, or C) those classes are the types of static members
    107      * in the class. However, exceptions that a class uses are always loaded when the class is loaded, even before
    108      * they are actually used.
    109      *
    110      * @param database The database to authenticate
    111      * @param username The username to authenticate with
    112      * @param password The password to authenticate with
    113      */
    114     static void authenticate(final DB database, final String username, final String password) {
    115         try {
    116             if (!database.authenticate(username, password.toCharArray())) {
    117                 LOGGER.error("Failed to authenticate against MongoDB server. Unknown error.");
    118             }
    119         } catch (final MongoException e) {
    120             LOGGER.error("Failed to authenticate against MongoDB: " + e.getMessage(), e);
    121         } catch (final IllegalStateException e) {
    122             LOGGER.error("Factory-supplied MongoDB database connection already authenticated with different" +
    123                     "credentials but lost connection.", e);
    124         }
    125     }
    126 }