// balm_collectorrepository.h                                         -*-C++-*-

// ----------------------------------------------------------------------------
//                                   NOTICE
//
// This component is not up to date with current BDE coding standards, and
// should not be used as an example for new development.
// ----------------------------------------------------------------------------

#ifndef INCLUDED_BALM_COLLECTORREPOSITORY
#define INCLUDED_BALM_COLLECTORREPOSITORY

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a repository for collectors.
//
//@CLASSES:
//   balm::CollectorRepository: a repository for collectors
//
//@SEE_ALSO: balm_collector, balm_integercollector, balm_metricsmanager
//
//@DESCRIPTION: This component defines a class, 'balm::CollectorRepository',
// that serves as a repository for 'balm::Collector' and
// 'balm::IntegerCollector' objects.  The collector repository supports
// operations to create and lookup collectors, as well as an operation to
// collect metric records from the collectors in the repository.  Collectors
// are identified by a metric id, which uniquely identifies the metric for
// which they collect values.  The 'getDefaultCollector' (and
// 'getDefaultIntegerCollector') operations return the default collector (or
// integer collector) for the supplied metric.  The 'addCollector' (and
// 'addIntegerCollector') operations create and return a new collector (or
// integer collector) for the specified metric.  Each collector instance can
// can safely collect values from multiple threads, however, the collector does
// use a mutex: Applications anticipating high contention for that lock can use
// 'addCollector' (and 'addIntegerCollector') to obtain multiple collectors and
// thereby reduce contention.  Finally, the 'collectAndReset' operation
// collects and returns metric records from each of the collectors in the
// repository.
//
///Alternative Systems for Telemetry
///---------------------------------
// Bloomberg software may alternatively use the GUTS telemetry API, which is
// integrated into Bloomberg infrastructure.
//
///Thread Safety
///-------------
// 'balm::CollectorRepository' is fully *thread-safe*, meaning that all
// non-creator operations on a given instance can be safely invoked
// simultaneously from multiple threads.
//
///Usage
///-----
// The following example illustrates creating a 'balm::CollectorRepository',
// then looking up collectors in that repository, and finally collecting values
// from the repository.  We start by creating a repository and looking up 2
// collectors and 2 integer collectors:
//..
//  bslma::Allocator *allocator = bslma::Default::allocator(0);
//  balm::MetricRegistry  metricRegistry(allocator);
//  balm::CollectorRepository repository(&metricRegistry, allocator);
//
//  balm::Collector *collector1 = repository.getDefaultCollector("Test", "C1");
//  balm::Collector *collector2 = repository.getDefaultCollector("Test", "C2");
//  balm::IntegerCollector *intCollector1 =
//                         repository.getDefaultIntegerCollector("Test", "C3");
//  balm::IntegerCollector *intCollector2 =
//                         repository.getDefaultIntegerCollector("Test", "C4");
//
//      assert(collector1    != collector2);
//      assert(collector1    == repository.getDefaultCollector("Test", "C1"));
//      assert(intCollector1 != intCollector2);
//      assert(intCollector1 ==
//             repository.getDefaultIntegerCollector("Test", "C3"));
//..
// We now update the values in those collectors:
//..
//  collector1->update(1.0);
//  collector1->update(2.0);
//  collector2->update(4.0);
//
//  intCollector1->update(5);
//  intCollector2->update(6);
//..
// We can use the repository to collect recorded values from the collectors it
// manages.  Since there are collectors for four metrics, there should be four
// recorded values.  Note the order in which the records are returned is
// undefined.
//..
//  bsl::vector<balm::MetricRecord> records(allocator);
//  repository.collectAndReset(&records, metricRegistry.getCategory("Test"));
//      assert(4 == records.size());
//..
// Finally we write the recorded values to the console:
//..
//  bsl::vector<balm::MetricRecord>::const_iterator it;
//  for (it = records.begin(); it != records.end(); ++it) {
//       bsl::cout << *it << bsl::endl;
//  }
//..
// The output of the for-loop should be:
//..
//  [ Test.C1: 2 3 1 2 ]
//  [ Test.C2: 1 4 4 4 ]
//  [ Test.C3: 1 5 5 5 ]
//  [ Test.C4: 1 6 6 6 ]
//..

#include <balscm_version.h>

#include <balm_collector.h>
#include <balm_integercollector.h>
#include <balm_metricid.h>
#include <balm_metricrecord.h>
#include <balm_metricregistry.h>

#include <bslmt_rwmutex.h>

#include <bslma_allocator.h>
#include <bslma_default.h>

#include <bslmf_nestedtraitdeclaration.h>

#include <bsl_map.h>
#include <bsl_memory.h>
#include <bsl_vector.h>

#include <bsls_libraryfeatures.h>

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
# include <memory_resource>
#endif

#include <vector>                   // 'std::vector', 'std::pmr::vector'

namespace BloombergLP {
namespace balm {

class Category;
class CollectorRepository_MetricCollectors;  // defined in implementation

                         // =========================
                         // class CollectorRepository
                         // =========================

class CollectorRepository {
    // This class defines a fully thread-safe repository mechanism for
    // 'Collector' and 'IntegerCollector' objects.  Collectors are identified
    // in the repository by a 'MetricId' object and also grouped together
    // according to the category of the metric.  This repository supports
    // operations to create, find, and collect metric records from the
    // collectors in the repository.

    // PRIVATE TYPES
    typedef CollectorRepository_MetricCollectors     MetricCollectors;
        // 'MetricCollectors' is an alias for the (private) implementation type
        // that contains the collectors and integer collectors for a single
        // metric id.

    typedef bsl::shared_ptr<MetricCollectors>             MetricCollectorsSPtr;
        // 'MetricCollectorsPtr' is an alias for a shared pointer to a
        // 'MetricRepository_MetricCollectors' object.

    typedef bsl::map<MetricId, MetricCollectorsSPtr> Collectors;
        // 'Collectors' is an alias for a map from a 'MetricId' object to the
        // collectors and integer collectors for that metric.

    typedef bsl::map<const Category *,
                     bsl::vector<MetricCollectors *> >  CategorizedCollectors;
        // 'CategorizedCollectors' is an alias for a map from a category to
        // the list of metric collectors belonging to that category.  Note
        // that each 'MetricCollectors' instance contains all the collectors
        // for a single metric.

    // DATA
    MetricRegistry         *d_registry_p;  // registry of ids (held, not owned)
    Collectors              d_collectors;  // collectors (owned)
    CategorizedCollectors   d_categories;  // map of category => collectors
    mutable bslmt::RWMutex  d_rwMutex;     // data lock
    bslma::Allocator       *d_allocator_p; // allocator (held, not owned)

    // NOT IMPLEMENTED
    CollectorRepository(const CollectorRepository& );
    CollectorRepository& operator=(const CollectorRepository& );

  private:
    // PRIVATE MANIPULATORS
    template <class VECTOR>
    void collectAndResetImp(VECTOR         *records,
                            const Category *category);
        // Append to the specified 'records' the collected metric record
        // values from the collectors in this repository belonging to the
        // specified 'category'; then reset those collectors to their default
        // values.

    template <class VECTOR>
    void collectImp(VECTOR         *records,
                    const Category *category);
        // Append to the specified 'records' the collected metric record
        // values from the collectors in this repository belonging to the
        // specified 'category'.  Note that this operation does not reset the
        // managed collectors, so subsequent collection operations will
        // effectively re-collect the current values.

    MetricCollectors& getMetricCollectors(const MetricId& metricId);
        // Return a reference to the modifiable collectors associated with the
        // specified 'metricId'.  If a collection of collectors for the
        // 'metricId' does not already exist, create one and add it to the map
        // of 'Collectors' ('d_collectors') and also the map of
        // 'CategorizedCollectors' ('d_categories').  The behavior is undefined
        // unless the calling thread has a *write* *lock* to 'd_rwMutex' and
        // 'metricId' is valid.

  public:
    // PUBLIC TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(CollectorRepository,
                                                    bslma::UsesBslmaAllocator);

    // CREATORS
    CollectorRepository(MetricRegistry        *registry,
                        bslma::Allocator      *basicAllocator = 0);
        // Create an empty collector repository that will use the specified
        // 'registry' to identify the metrics for which it manages collectors.
        // Optionally specify a 'basicAllocator' used to supply memory.  If
        // 'basicAllocator' is 0, the currently installed default allocator is
        // used.  The behavior is undefined if 'registry' is 0.

    ~CollectorRepository();
        // Free all the collectors in this repository and destroy this object.

    // MANIPULATORS
    void collectAndReset(bsl::vector<MetricRecord>      *records,
                         const Category                 *category);
    void collectAndReset(std::vector<MetricRecord>      *records,
                         const Category                 *category);
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
    void collectAndReset(std::pmr::vector<MetricRecord> *records,
                         const Category                 *category);
#endif
        // Append to the specified 'records' the collected metric record
        // values from the collectors in this repository belonging to the
        // specified 'category'; then reset those collectors to their default
        // values.

    void collect(bsl::vector<MetricRecord>      *records,
                 const Category                 *category);
    void collect(std::vector<MetricRecord>      *records,
                 const Category                 *category);
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
    void collect(std::pmr::vector<MetricRecord> *records,
                 const Category                 *category);
#endif
        // Append to the specified 'records' the collected metric record
        // values from the collectors in this repository belonging to the
        // specified 'category'.  Note that this operation does not reset the
        // managed collectors, so subsequent collection operations will
        // effectively re-collect the current values.

    Collector *getDefaultCollector(const char *category,
                                   const char *metricName);
        // Return the address of the modifiable default collector identified by
        // the specified null-terminated strings 'category' and 'metricName'.
        // If a collector for the identified metric does not already exist in
        // the repository, create one, add it to the repository, and return its
        // address.  In addition, if the identified metric has not already been
        // registered, add the identified metric to the 'metricRegistry'
        // supplied at construction.  Note that this operation is logically
        // equivalent to:
        //..
        //  getDefaultCollector(registry().getId(category, metricName))
        //..

    Collector *getDefaultCollector(const MetricId& metricId);
        // Return the address of the modifiable default collector identified by
        // the specified 'metricId'.  If a default collector for the identified
        // metric does not already exist in the repository, create one, add it
        // to the repository, and return its address.

    IntegerCollector *getDefaultIntegerCollector(const char *category,
                                                 const char *metricName);
        // Return the address of the modifiable default integer collector
        // identified by the specified 'category' and 'metricName'.  If a
        // default integer collector for the identified metric does not
        // already exist in the repository, create one, add it to the
        // repository, and return its address.  In addition, if the identified
        // metric has not already been registered, add the identified metric
        // to the 'metricRegistry' supplied at construction.  The behavior is
        // undefined unless 'category' and 'metricName' are null-terminated.
        // Note that this operation is logically equivalent to:
        //..
        //  getDefaultIntegerCollector(registry().getId(category, metricName))
        //..

    IntegerCollector *getDefaultIntegerCollector(const MetricId& metricId);
        // Return the address of the modifiable default integer collector
        // identified by the specified 'metricId'.  If a default integer
        // collector for the identified metric does not already exist in the
        // repository, create one, add it to the repository, and return its
        // address.

    bsl::shared_ptr<Collector> addCollector(const char *category,
                                            const char *metricName);
        // Return a shared pointer to a newly-created modifiable collector
        // identified by the specified null-terminated strings 'category' and
        // 'metricName', and add that collector to the repository.  If is not
        // already registered, also add the identified metric to the
        // 'metricRegistry' supplied at construction.  Note that this operation
        // is logically equivalent to:
        //..
        //  addCollector(registry().getId(category, metricName))
        //..

    bsl::shared_ptr<Collector> addCollector(const MetricId& metricId);
        // Return a shared pointer to a newly-created modifiable collector
        // identified by the specified 'metricId' and add that collector to the
        // repository.  The behavior is undefined unless 'metricId' is a valid
        // id returned by the 'MetricRepository' supplied at construction.

    bsl::shared_ptr<IntegerCollector> addIntegerCollector(
                                                       const char *category,
                                                       const char *metricName);
        // Return a shared pointer to a newly created modifiable integer
        // collector identified by the specified 'category' and 'metricName'
        // and add that collector to the repository.  If is not already
        // registered, also add the identified metric to the 'metricRegistry'
        // supplied at construction.  The behavior is undefined unless
        // 'category' and 'metricName' are null-terminated.  Note that this
        // operation is logically equivalent to:
        //..
        //  addIntegerCollector(registry().getId(category, metricName))
        //..

    bsl::shared_ptr<IntegerCollector> addIntegerCollector(
                                                     const MetricId& metricId);
        // Return a shared pointer to a newly-created modifiable collector
        // identified by the specified 'metricId' and add that collector to the
        // repository.  The behavior is undefined unless 'metricId' is a valid
        // id returned by the 'MetricRepository' supplied at construction.

    int getAddedCollectors(
          bsl::vector<bsl::shared_ptr<Collector> >              *collectors,
          bsl::vector<bsl::shared_ptr<IntegerCollector> >       *intCollectors,
          const MetricId&                                        metricId);
    int getAddedCollectors(
          std::vector<bsl::shared_ptr<Collector> >              *collectors,
          std::vector<bsl::shared_ptr<IntegerCollector> >       *intCollectors,
          const MetricId&                                        metricId);
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
    int getAddedCollectors(
          std::pmr::vector<bsl::shared_ptr<Collector> >         *collectors,
          std::pmr::vector<bsl::shared_ptr<IntegerCollector> >  *intCollectors,
          const MetricId&                                        metricId);
#endif
        // Append to the specified 'collectors' and 'intCollectors' shared
        // pointers to any collectors, and integer collectors, collecting
        // values for the metrics identified by the specified 'metricId' that
        // were added using the 'addCollector' or 'addIntegerCollector'
        // methods, and return the combined total number of collectors and
        // integer collectors that were found.  This method does *not* count
        // or return the default collectors for 'metricId'.  The behavior is
        // undefined unless 'metricId' is a valid id returned by the
        // 'MetricRepository' supplied at construction.

    MetricRegistry& registry();
        // Return a reference to the modifiable registry of metrics used by
        // this collector repository.

    // ACCESSORS
    const MetricRegistry& registry() const;
        // Return a reference to the non-modifiable registry of metrics used by
        // this collector repository.
};

// ============================================================================
//                            INLINE DEFINITIONS
// ============================================================================

                         // -------------------------
                         // class CollectorRepository
                         // -------------------------

// CREATORS
inline
CollectorRepository::CollectorRepository(MetricRegistry   *registry,
                                         bslma::Allocator *basicAllocator)
: d_registry_p(registry)
, d_collectors(basicAllocator)
, d_categories(basicAllocator)
, d_rwMutex()
, d_allocator_p(bslma::Default::allocator(basicAllocator))
{
}

inline
CollectorRepository::~CollectorRepository()
{
}

// MANIPULATORS
inline
Collector *CollectorRepository::getDefaultCollector(const char *category,
                                                    const char *metricName)
{
    return getDefaultCollector(d_registry_p->getId(category, metricName));
}

inline
IntegerCollector *CollectorRepository::getDefaultIntegerCollector(
                                                        const char *category,
                                                        const char *metricName)
{
    return getDefaultIntegerCollector(d_registry_p->getId(category,
                                                          metricName));
}

inline
bsl::shared_ptr<Collector> CollectorRepository::addCollector(
                                                        const char *category,
                                                        const char *metricName)
{
    return addCollector(d_registry_p->getId(category, metricName));
}

inline
bsl::shared_ptr<IntegerCollector>
CollectorRepository::addIntegerCollector(const char *category,
                                         const char *metricName)
{
    return addIntegerCollector(d_registry_p->getId(category, metricName));
}

inline
MetricRegistry& CollectorRepository::registry()
{
    return *d_registry_p;
}

// ACCESSORS
inline
const MetricRegistry& CollectorRepository::registry() const
{
    return *d_registry_p;
}
}  // close package namespace

}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2015 Bloomberg Finance L.P.
//
// 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.
// ----------------------------- END-OF-FILE ----------------------------------