SQLQueryCounterConfiguration.java

/*
** Module   : SQLQueryCounterConfiguration.java
** Abstract : Reading configuration for QueryCounterJMX.
**
** Copyright (c) 2024, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------------Description---------------------------------------
** 001 ICP 20241111 First revision with basic usage.
*/

/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
**   Under Section 7 of the GNU Affero GPL version 3, the following additional
**   terms apply to the works covered under the License.  These additional terms
**   are non-permissive additional terms allowed under Section 7 of the GNU
**   Affero GPL version 3 and may not be removed by you.
**
**   0. Attribution Requirement.
**
**     You must preserve all legal notices or author attributions in the covered
**     work or Appropriate Legal Notices displayed by works containing the covered
**     work.  You may not remove from the covered work any author or developer
**     credit already included within the covered work.
**
**   1. No License To Use Trademarks.
**
**     This license does not grant any license or rights to use the trademarks
**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
**     of Golden Code Development Corporation. You are not authorized to use the
**     name Golden Code, FWD, or the names of any author or contributor, for
**     publicity purposes without written authorization.
**
**   2. No Misrepresentation of Affiliation.
**
**     You may not represent yourself as Golden Code Development Corporation or FWD.
**
**     You may not represent yourself for publicity purposes as associated with
**     Golden Code Development Corporation, FWD, or any author or contributor to
**     the covered work, without written authorization.
**
**   3. No Misrepresentation of Source or Origin.
**
**     You may not represent the covered work as solely your work.  All modified
**     versions of the covered work must be marked in a reasonable way to make it
**     clear that the modified work is not originating from Golden Code Development
**     Corporation or FWD.  All modified versions must contain the notices of
**     attribution required in this license.
*/

package com.goldencode.p2j.persist.orm;

import static com.goldencode.p2j.util.logging.LoggingUtil.resolveLogFile;

import com.goldencode.p2j.directory.DirectoryService;
import com.goldencode.p2j.jmx.*;
import com.goldencode.p2j.main.ClientCore;
import com.goldencode.p2j.util.Utils;
import com.goldencode.p2j.util.logging.CentralLogger;

import java.util.*;
import java.util.function.Supplier;
import java.util.logging.Level;

/**
 * Class responsible with reading configuration for {@link QueryCounterJMX}.
 */
public final class SQLQueryCounterConfiguration {

   /** Map to hold the configuration for each database. */
   private static final Map<String, SQLQueryCounterConfiguration> configs = new HashMap<>();

   /** Logger. */
   private static final CentralLogger LOG = CentralLogger.get(SQLQueryCounterConfiguration.class);

   /** Global directory path, used if a local one does not exist. */
   private static final String GLOBAL_DIR_PATH = "persistence/query-counting";

   /** Singleton instance of {@link QueryCounterJMX} for managing query counting operations. */
   private static final QueryCounterJMX SIMPLE_QUERY_COUNTER =
         QueryCounterJMX.getInstance(FwdServerJMX.QueryCounterEnum.QueryCounter);

   /** Flag to enable or disable query counting. */
   private boolean enableCounting;

   /** The file that will receive the logging output. */
   private String outputFile;

   /**
    * Constructor of a configuration.
    *
    * Enables logging based on the directory configuration.
    *
    * @param   databaseName
    *          The name of the database in use.
    */
   private SQLQueryCounterConfiguration(String databaseName)
   {
      boolean enableQueryCounting = false;
      String outputFile = null;
      String directoryPath = null;
      boolean hasLocalConfiguration = false;
      String localdirectoryPath = "database/" + databaseName + "/query-counting";
      String defaultOutputFile = "%db_querycount.csv";
      DirectoryService ds = DirectoryService.getInstance();

      if (ds != null)
      {
         try
         {
            if (!ds.bind())
            {
               LOG.warning("Query counting configuration is not enabled");
            }

            String isCountingEnabled = Utils.getDirectoryNodeString(ds,
                                                                    localdirectoryPath + "/enabled",
                                                                    "NULL",
                                                                    false);
            switch (isCountingEnabled.toUpperCase())
            {
               case "NULL":
                  String globalPath = GLOBAL_DIR_PATH + "/enabled";
                  enableQueryCounting = Boolean.parseBoolean(Utils.getDirectoryNodeString(ds,
                                                                                          globalPath,
                                                                                          "FALSE",
                                                                                          false));
                  break;
               case "TRUE":
                  enableQueryCounting = true;
                  hasLocalConfiguration = true;
                  break;
               case "FALSE":
                  enableQueryCounting = false;
                  hasLocalConfiguration = true;
                  break;
               default:
                  throw new RuntimeException("Invalid value for attribute: enabled");
            }
         }
         finally
         {
            ds.unbind();
         }
      }
      enableCounting = enableQueryCounting;
      if (enableCounting)
      {
         directoryPath = hasLocalConfiguration ? localdirectoryPath : GLOBAL_DIR_PATH;
         outputFile = Utils.getDirectoryNodeString(ds,
                                                   directoryPath + "/output-to-file",
                                                   defaultOutputFile,
                                                   false);

      }
      Supplier<Long> processIdSupplier = () ->
      {
         try
         {
            System.loadLibrary("p2j");
            return ClientCore.getPid();
         }
         catch (Throwable t)
         {
            LOG.log(Level.FINE, "", t);
            return 0L;
         }
      };

      String formattedFilePath = resolveLogFile(outputFile,
                                                processIdSupplier,
                                                "server",
                                                databaseName,
                                                true);

      outputFile = formattedFilePath;
      SIMPLE_QUERY_COUNTER.updateConfiguration(databaseName, new QueryCountConfiguration(enableCounting, outputFile));
   }

   /**
    * Get the query counting configuration of a specific database.
    *
    * @param   databaseName
    *          The name of the database in use.
    *
    * @return  SQLQueryCounterConfiguration
    *          If it exists, an already created configuration for that database, or a new one otherwise.
    */
   public static QueryCountConfiguration getConfiguration(String databaseName)
   {
      SQLQueryCounterConfiguration sqlConfig = configs.computeIfAbsent(databaseName, SQLQueryCounterConfiguration::new);
      return new QueryCountConfiguration(sqlConfig.isQueryCountingEnabled(), sqlConfig.getOutputFile());
   }

   /**
    * Getter for the {@code enableCounting} field.
    *
    * @return  boolean
    *          The value of {@code enableCounting}.
    */
   public boolean isQueryCountingEnabled()
   {
      return enableCounting;
   }

   /**
    * Getter for the {@code outputFile} field.
    *
    * @return  String
    *          The value of {@code outputFile}.
    */
   public String getOutputFile()
   {
      return outputFile;
   }
}