blob: 0d7eb17632abea0f6b60599e560038e0fc17e352 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
#define __STDC_CONSTANT_MACROS
#include <log4cxx/logstring.h>
#include <log4cxx/helpers/filewatchdog.h>
#include <log4cxx/helpers/loglog.h>
#include <log4cxx/helpers/transcoder.h>
#include <log4cxx/helpers/exception.h>
#include <log4cxx/helpers/threadutility.h>
#include <log4cxx/helpers/stringhelper.h>
#include <memory>
#include <functional>
#include <chrono>
#include <thread>
#include <condition_variable>
using namespace LOG4CXX_NS;
using namespace LOG4CXX_NS::helpers;
long FileWatchdog::DEFAULT_DELAY = 60000;
struct FileWatchdog::FileWatchdogPrivate
{
FileWatchdogPrivate
( const File& filename
, const spi::ConfiguratorPtr& processor
, const spi::LoggerRepositoryPtr& target
)
: file(filename)
, taskName{ LOG4CXX_STR("WatchDog_") + filename.getName() }
, pConfigurator(processor)
, pRepository(target)
{ }
File file;
long millisecondDelay{ DEFAULT_DELAY };
log4cxx_time_t lastModif{ 0 };
bool warnedAlready { false };
LogString taskName;
ThreadUtility::ManagerWeakPtr taskManager;
/**
Serialize access and modification to: file, lastModif and warnedAlready.
Recursive because doOnChange may re-enter watchdog methods.
*/
std::recursive_mutex mutex;
/// The configuration file processor
spi::ConfiguratorPtr pConfigurator;
/// The configuration target
spi::LoggerRepositoryPtr pRepository;
/// The result of the most recent spi::Configurator::doConfigure call.
spi::ConfigurationStatus configResult{ spi::ConfigurationStatus::NotConfigured };
/// Call checkAndConfigure() on \c parent and then add an asynchronous task that periodically checks for a file change.
void start(const FileWatchdogPtr& parent);
};
FileWatchdog::FileWatchdog
( const File& filename
, const spi::ConfiguratorPtr& processor
, const spi::LoggerRepositoryPtr& target
)
: m_priv{ std::make_shared<FileWatchdogPrivate>(filename, processor, target) }
{
}
FileWatchdog::~FileWatchdog()
{
stop();
}
bool FileWatchdog::is_active()
{
bool result = false;
if (auto p = m_priv->taskManager.lock())
result = p->value().hasPeriodicTask(m_priv->taskName);
return result;
}
void FileWatchdog::stop()
{
if (auto p = m_priv->taskManager.lock())
p->value().removePeriodicTask(m_priv->taskName);
}
/**
Stop all tasks that periodically checks for a file change.
*/
void FileWatchdog::stopAll()
{
ThreadUtility::instance()->removePeriodicTasksMatching(LOG4CXX_STR("WatchDog_"));
}
const File& FileWatchdog::file()
{
return m_priv->file;
}
void FileWatchdog::checkAndConfigure()
{
std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
if (LogLog::isDebugEnabled())
{
LogString msg(LOG4CXX_STR("Checking ["));
msg += m_priv->file.getPath();
msg += LOG4CXX_STR("]");
LogLog::debug(msg);
}
if (!m_priv->file.exists())
{
if (!m_priv->warnedAlready)
{
LogLog::warn(LOG4CXX_STR("[")
+ m_priv->file.getPath()
+ LOG4CXX_STR("] does not exist."));
m_priv->warnedAlready = true;
}
}
else
{
auto thisMod = m_priv->file.lastModified();
if (thisMod > m_priv->lastModif)
{
m_priv->lastModif = thisMod;
doOnChange();
m_priv->warnedAlready = false;
}
}
}
auto FileWatchdog::getSharedPtr() -> FileWatchdogPtr
{
// The aliasing constructor returns a pointer to 'this' (FileWatchdog*)
// but increments/decrements the ref-count of 'm_priv'.
return std::shared_ptr<FileWatchdog>(m_priv, this);
}
void FileWatchdog::start()
{
m_priv->start(getSharedPtr());
}
void FileWatchdog::FileWatchdogPrivate::start(const FileWatchdogPtr& parent)
{
auto taskManager = ThreadUtility::instancePtr();
parent->checkAndConfigure();
if (!taskManager->value().hasPeriodicTask(this->taskName))
{
if (LogLog::isDebugEnabled())
{
LogString msg(LOG4CXX_STR("Checking ["));
msg += this->file.getPath();
msg += LOG4CXX_STR("] at ");
StringHelper::toString((int)this->millisecondDelay, msg);
msg += LOG4CXX_STR(" ms interval");
LogLog::debug(msg);
}
taskManager->value().addPeriodicTask(this->taskName
, [parent](){ parent->checkAndConfigure(); }
, std::chrono::milliseconds(this->millisecondDelay)
);
this->taskManager = taskManager;
}
}
void FileWatchdog::setDelay(long millisecondDelay)
{
m_priv->millisecondDelay = millisecondDelay;
auto p = m_priv->taskManager.lock();
if (p && p->value().hasPeriodicTask(m_priv->taskName))
{
p->value().removePeriodicTask(m_priv->taskName);
auto pThis = getSharedPtr();
p->value().addPeriodicTask(m_priv->taskName
, [pThis](){ pThis->checkAndConfigure(); }
, std::chrono::milliseconds(m_priv->millisecondDelay)
);
}
}
void FileWatchdog::setFile(const File& newValue)
{
std::lock_guard<std::recursive_mutex> lock(m_priv->mutex);
if (m_priv->file.getPath() != newValue.getPath())
{
m_priv->file = newValue;
m_priv->lastModif = 0;
}
}
void FileWatchdog::doOnChange()
{
if (m_priv->pConfigurator)
m_priv->configResult = m_priv->pConfigurator->doConfigure(m_priv->file, m_priv->pRepository);
else
m_priv->configResult = spi::ConfigurationStatus::NotConfigured;
}
auto FileWatchdog::startWatching
( const File& filename
, const spi::ConfiguratorPtr& processor
, const spi::LoggerRepositoryPtr& target
, long millisecondDelay
) -> spi::ConfigurationStatus
{
auto pDog = std::shared_ptr<FileWatchdog>(new FileWatchdog(filename, processor, target));
if (0 < millisecondDelay)
pDog->setDelay(millisecondDelay);
pDog->m_priv->start(pDog);
return pDog->getStatus();
}
auto FileWatchdog::getStatus() -> spi::ConfigurationStatus
{
return m_priv->configResult;
}