All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.helger.phoss.smp.backend.mongodb.audit.IDFactoryMongoDB Maven / Gradle / Ivy

There is a newer version: 7.1.6
Show newest version
/*
 * Copyright (C) 2019-2024 Philip Helger and contributors
 * philip[at]helger[dot]com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.helger.phoss.smp.backend.mongodb.audit;

import java.util.Date;

import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;

import org.bson.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.helger.commons.ValueEnforcer;
import com.helger.commons.annotation.Nonempty;
import com.helger.commons.hashcode.HashCodeGenerator;
import com.helger.commons.id.factory.AbstractPersistingLongIDFactory;
import com.helger.commons.string.ToStringGenerator;
import com.helger.phoss.smp.backend.mongodb.MongoClientSingleton;
import com.mongodb.client.MongoCollection;

/**
 * Implementation of a long ID factory using MongoDB
 *
 * @author Philip Helger
 * @since 5.2.1
 */
public class IDFactoryMongoDB extends AbstractPersistingLongIDFactory
{
  /** The default number of values to reserve with a single IO action */
  public static final int DEFAULT_RESERVE_COUNT = 20;

  /** The default collection name if none is provided */
  public static final String DEFAULT_COLLECTION_NAME = "smp-settings";

  private static final Logger LOGGER = LoggerFactory.getLogger (IDFactoryMongoDB.class);

  private static final String BSON_ID = "id";
  private static final String BSON_LONG_VALUE = "longvalue";
  private static final String VALUE_ID_LONG_ID = "long-id";

  private final String m_sCollectionName;
  private final transient MongoCollection  m_aCollection;
  private final long m_nInitialCount;

  /**
   * Default constructor using {@link #DEFAULT_COLLECTION_NAME} as the
   * collection name.
   *
   * @param nInitialCount
   *        Initial count to be used, if no MongoDB document exists. Must be
   *        ≥ 0.
   */
  public IDFactoryMongoDB (@Nonnegative final long nInitialCount)
  {
    this (DEFAULT_COLLECTION_NAME, DEFAULT_RESERVE_COUNT, nInitialCount);
  }

  /**
   * Constructor
   *
   * @param sCollectionName
   *        Collection name to use. May neither be null nor empty.
   * @param nReserveCount
   *        The number of IDs to reserve per persistence layer access. Must be
   *        > 0.
   * @param nInitialCount
   *        Initial count to be used, if no MongoDB document exists. Must be
   *        ≥ 0.
   */
  public IDFactoryMongoDB (@Nonnull @Nonempty final String sCollectionName,
                           @Nonnegative final int nReserveCount,
                           @Nonnegative final long nInitialCount)
  {
    super (nReserveCount);
    ValueEnforcer.notEmpty (sCollectionName, "CollectionName");
    ValueEnforcer.isGE0 (nInitialCount, "InitialCount");
    m_sCollectionName = sCollectionName;
    m_aCollection = MongoClientSingleton.getInstance ().getCollection (sCollectionName);
    m_nInitialCount = nInitialCount;
  }

  @Override
  protected long readAndUpdateIDCounter (@Nonnegative final int nReserveCount)
  {
    // Existing value
    final long nRead;
    final Document aFilter = new Document (BSON_ID, VALUE_ID_LONG_ID);
    Document aDoc = m_aCollection.find (aFilter).first ();
    if (aDoc != null)
    {
      final Long aLong = aDoc.getLong (BSON_LONG_VALUE);
      nRead = aLong != null ? aLong.longValue () : m_nInitialCount;
    }
    else
      nRead = m_nInitialCount;

    final boolean bCreate = aDoc == null;
    if (bCreate)
    {
      aDoc = new Document ();
      aDoc.append (BSON_ID, VALUE_ID_LONG_ID);
    }
    aDoc.remove (BSON_LONG_VALUE);
    final long nNewValue = nRead + nReserveCount;
    aDoc.append (BSON_LONG_VALUE, Long.valueOf (nNewValue));
    aDoc.append ("last-modification", new Date ());
    if (bCreate)
    {
      if (!m_aCollection.insertOne (aDoc).wasAcknowledged ())
        throw new IllegalStateException ("Failed to insert into MongoDB Collection");
    }
    else
    {
      if (!m_aCollection.replaceOne (aFilter, aDoc).wasAcknowledged ())
        throw new IllegalStateException ("Failed to replace in MongoDB Collection");
    }

    if (LOGGER.isDebugEnabled ())
      LOGGER.debug ("Updated MongoDB ID to " + nNewValue);

    return nRead;
  }

  @Override
  public boolean equals (final Object o)
  {
    if (o == this)
      return true;
    if (!super.equals (o))
      return false;
    final IDFactoryMongoDB rhs = (IDFactoryMongoDB) o;
    return m_sCollectionName.equals (rhs.m_sCollectionName);
  }

  @Override
  public int hashCode ()
  {
    return HashCodeGenerator.getDerived (super.hashCode ()).append (m_sCollectionName).getHashCode ();
  }

  @Override
  public String toString ()
  {
    return ToStringGenerator.getDerived (super.toString ()).append ("CollectionName", m_sCollectionName).getToString ();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy