This is an automated email from the ASF dual-hosted git repository.

swebb2066 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-log4cxx.git


The following commit(s) were added to refs/heads/master by this push:
     new 2e70889a Prevent abnormal termination when the XML configuration file 
has recursive references (#605)
2e70889a is described below

commit 2e70889a4dd9f3f5d4c2a3c7898d7bd9791d822f
Author: Stephen Webb <[email protected]>
AuthorDate: Thu Mar 12 09:41:19 2026 +1100

    Prevent abnormal termination when the XML configuration file has recursive 
references (#605)
    
    * Detect and ignore a recursive reference to an appender
    
    * Silence ABI check
---
 src/main/cpp/domconfigurator.cpp                 | 514 +++++++++++++++--------
 src/main/include/log4cxx/xml/domconfigurator.h   |  14 +-
 src/test/cpp/asyncappendertestcase.cpp           |  70 ++-
 src/test/resources/input/xml/recursiveConfig.xml |  38 ++
 4 files changed, 433 insertions(+), 203 deletions(-)

diff --git a/src/main/cpp/domconfigurator.cpp b/src/main/cpp/domconfigurator.cpp
index d54a092a..6e1ad59f 100644
--- a/src/main/cpp/domconfigurator.cpp
+++ b/src/main/cpp/domconfigurator.cpp
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include <log4cxx/logstring.h>
 #include <log4cxx/xml/domconfigurator.h>
 #include <log4cxx/appender.h>
 #include <log4cxx/asyncappender.h>
@@ -63,12 +62,75 @@ using namespace LOG4CXX_NS::rolling;
 
 #define MAX_ATTRIBUTE_NAME_LEN 2000
 
+using FilterStore = std::vector<FilterPtr>;
+
 struct DOMConfigurator::DOMConfiguratorPrivate
 {
-       helpers::Properties props = Configurator::properties();
-       spi::LoggerRepositoryPtr repository;
-       spi::LoggerFactoryPtr loggerFactory;
+public: // Types
+       struct AppenderStatus
+       {
+               AppenderPtr pAppender;
+               bool activated;
+       };
+       using AppenderMap = std::map<LogString, AppenderStatus>;
+
+public: // Attributes
+       Properties props = Configurator::properties();
+       LoggerRepositoryPtr repository;
+#if LOG4CXX_ABI_VERSION <= 15
+       LoggerFactoryPtr loggerFactory{ 
std::make_shared<DefaultLoggerFactory>() };
+#else
+       LoggerFactoryPtr loggerFactory{ std::make_shared<LoggerFactory>() };
+#endif
        bool appenderAdded{ false };
+       AppenderMap     appenders;
+       Pool p;
+       CharsetDecoderPtr utf8Decoder{ CharsetDecoder::getUTF8Decoder() };
+       apr_xml_doc* doc{ nullptr };
+
+public: // ...structor
+       DOMConfiguratorPrivate()
+               : repository(LogManager::getLoggerRepository())
+       {}
+       
+       DOMConfiguratorPrivate(const LoggerRepositoryPtr& r)
+               : repository(r)
+       {}
+
+public: // Methods
+       AppenderPtr findAppenderByName(apr_xml_elem* elem, const LogString& 
appenderName);
+
+       AppenderPtr findAppenderByReference(apr_xml_elem* appenderRef);
+
+       AppenderPtr parseAppender(apr_xml_elem* appenderElement);
+
+       void parseErrorHandler(apr_xml_elem* element, const AppenderPtr& 
appender);
+
+       FilterStore parseFilters(apr_xml_elem* element);
+
+       void parseLogger(apr_xml_elem* loggerElement);
+
+       void parseLoggerFactory(apr_xml_elem* factoryElement);
+
+       ObjectPtr parseTriggeringPolicy(apr_xml_elem* factoryElement);
+
+       RollingPolicyPtr parseRollingPolicy(apr_xml_elem* factoryElement);
+
+       void parseRoot(apr_xml_elem* rootElement);
+
+       void parseChildrenOfLoggerElement(apr_xml_elem* catElement, LoggerPtr 
logger, bool isRoot);
+
+       LayoutPtr parseLayout(apr_xml_elem* layout_element);
+
+       void parseLevel(apr_xml_elem* element, LoggerPtr logger, bool isRoot);
+
+       void setParameter(apr_xml_elem* elem, config::PropertySetter& 
propSetter);
+
+       void parse(apr_xml_elem* element);
+
+       LogString getAttribute(apr_xml_elem*, const std::string& attrName);
+
+       LogString subst(const LogString& value);
 };
 
 namespace LOG4CXX_NS
@@ -141,42 +203,34 @@ IMPLEMENT_LOG4CXX_OBJECT(DOMConfigurator)
 #define INTERNAL_COLOR_ATTR "color"
 #define THREAD_CONFIG_ATTR "threadConfiguration"
 
-DOMConfigurator::DOMConfigurator()
-       : m_priv(std::make_unique<DOMConfiguratorPrivate>())
-{
-}
+DOMConfigurator::DOMConfigurator() {}
 
 DOMConfigurator::~DOMConfigurator() {}
 
 /**
 Used internally to parse appenders by IDREF name.
 */
-AppenderPtr DOMConfigurator::findAppenderByName(LOG4CXX_NS::helpers::Pool& p,
-       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
-       apr_xml_elem* element,
-       apr_xml_doc* doc,
-       const LogString& appenderName,
-       AppenderMap& appenders)
+AppenderPtr 
DOMConfigurator::DOMConfiguratorPrivate::findAppenderByName(apr_xml_elem* 
element, const LogString& appenderName)
 {
        AppenderPtr appender;
        std::string tagName(element->name);
 
        if (tagName == APPENDER_TAG)
        {
-               if (appenderName == getAttribute(utf8Decoder, element, 
NAME_ATTR))
+               if (appenderName == getAttribute(element, NAME_ATTR))
                {
-                       appender = parseAppender(p, utf8Decoder, element, doc, 
appenders);
+                       appender = parseAppender(element);
                }
        }
 
        if (element->first_child && !appender)
        {
-               appender = findAppenderByName(p, utf8Decoder, 
element->first_child, doc, appenderName, appenders);
+               appender = findAppenderByName(element->first_child, 
appenderName);
        }
 
        if (element->next && !appender)
        {
-               appender = findAppenderByName(p, utf8Decoder, element->next, 
doc, appenderName, appenders);
+               appender = findAppenderByName(element->next, appenderName);
        }
 
        return appender;
@@ -185,15 +239,10 @@ AppenderPtr 
DOMConfigurator::findAppenderByName(LOG4CXX_NS::helpers::Pool& p,
 /**
  Used internally to parse appenders by IDREF element.
 */
-AppenderPtr DOMConfigurator::findAppenderByReference(
-       LOG4CXX_NS::helpers::Pool& p,
-       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
-       apr_xml_elem* appenderRef,
-       apr_xml_doc* doc,
-       AppenderMap& appenders)
+AppenderPtr 
DOMConfigurator::DOMConfiguratorPrivate::findAppenderByReference(apr_xml_elem* 
appenderRef)
 {
        AppenderPtr appender;
-       LogString appenderName(subst(getAttribute(utf8Decoder, appenderRef, 
REF_ATTR)));
+       LogString appenderName(subst(getAttribute(appenderRef, REF_ATTR)));
        if (appenderName.empty())
        {
                LogString msg(LOG4CXX_STR("["));
@@ -208,16 +257,17 @@ AppenderPtr DOMConfigurator::findAppenderByReference(
 
        if (match != appenders.end())
        {
-               appender = match->second;
+               if (!match->second.activated)
+               {
+                       LogString msg(LOG4CXX_STR("Ignoring recursive reference 
to [") + appenderName + LOG4CXX_STR("]"));
+                       LogLog::warn(msg);
+               }
+               else
+                       appender = match->second.pAppender;
        }
        else if (doc)
        {
-               appender = findAppenderByName(p, utf8Decoder, doc->root, doc, 
appenderName, appenders);
-
-               if (appender)
-               {
-                       appenders.insert(AppenderMap::value_type(appenderName, 
appender));
-               }
+               appender = findAppenderByName(doc->root, appenderName);
        }
 
        if (!appender)
@@ -232,14 +282,10 @@ AppenderPtr DOMConfigurator::findAppenderByReference(
 /**
 Used internally to parse an appender element.
 */
-AppenderPtr DOMConfigurator::parseAppender(Pool& p,
-       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
-       apr_xml_elem* appenderElement,
-       apr_xml_doc* doc,
-       AppenderMap& appenders)
+AppenderPtr 
DOMConfigurator::DOMConfiguratorPrivate::parseAppender(apr_xml_elem* 
appenderElement)
 {
 
-       LogString className(subst(getAttribute(utf8Decoder, appenderElement, 
CLASS_ATTR)));
+       LogString className(subst(getAttribute(appenderElement, CLASS_ATTR)));
        if (LogLog::isDebugEnabled())
        {
                LogLog::debug(LOG4CXX_STR("Desired ") + 
Appender::getStaticClass().getName()
@@ -256,7 +302,8 @@ AppenderPtr DOMConfigurator::parseAppender(Pool& p,
                }
                PropertySetter propSetter(appender);
 
-               appender->setName(subst(getAttribute(utf8Decoder, 
appenderElement, NAME_ATTR)));
+               appender->setName(subst(getAttribute(appenderElement, 
NAME_ATTR)));
+               appenders.emplace(appender->getName(), AppenderStatus{appender, 
false});
 
                for (apr_xml_elem* currentElement = 
appenderElement->first_child;
                        currentElement;
@@ -268,18 +315,17 @@ AppenderPtr DOMConfigurator::parseAppender(Pool& p,
                        // Parse appender parameters
                        if (tagName == PARAM_TAG)
                        {
-                               setParameter(p, utf8Decoder, currentElement, 
propSetter);
+                               setParameter(currentElement, propSetter);
                        }
                        // Set appender layout
                        else if (tagName == LAYOUT_TAG)
                        {
-                               appender->setLayout(parseLayout(p, utf8Decoder, 
currentElement));
+                               
appender->setLayout(parseLayout(currentElement));
                        }
                        // Add filters
                        else if (tagName == FILTER_TAG)
                        {
-                               std::vector<LOG4CXX_NS::spi::FilterPtr> filters;
-                               parseFilters(p, utf8Decoder, currentElement, 
filters);
+                               auto filters = parseFilters(currentElement);
 
                                for (auto& item : filters)
                                {
@@ -288,11 +334,11 @@ AppenderPtr DOMConfigurator::parseAppender(Pool& p,
                        }
                        else if (tagName == ERROR_HANDLER_TAG)
                        {
-                               parseErrorHandler(p, utf8Decoder, 
currentElement, appender, doc, appenders);
+                               parseErrorHandler(currentElement, appender);
                        }
                        else if (tagName == ROLLING_POLICY_TAG)
                        {
-                               RollingPolicyPtr 
rollPolicy(parseRollingPolicy(p, utf8Decoder, currentElement));
+                               auto rollPolicy = 
parseRollingPolicy(currentElement);
                                RollingFileAppenderPtr rfa = 
LOG4CXX_NS::cast<RollingFileAppender>(appender);
 
                                if (rfa != NULL)
@@ -302,7 +348,7 @@ AppenderPtr DOMConfigurator::parseAppender(Pool& p,
                        }
                        else if (tagName == TRIGGERING_POLICY_TAG)
                        {
-                               ObjectPtr policy(parseTriggeringPolicy(p, 
utf8Decoder, currentElement));
+                               auto policy = 
parseTriggeringPolicy(currentElement);
                                RollingFileAppenderPtr rfa = 
LOG4CXX_NS::cast<RollingFileAppender>(appender);
                                TriggeringPolicyPtr policyPtr = 
LOG4CXX_NS::cast<TriggeringPolicy>(policy);
 
@@ -326,7 +372,7 @@ AppenderPtr DOMConfigurator::parseAppender(Pool& p,
                                if 
(appender->instanceof(AppenderAttachable::getStaticClass()))
                                {
                                        AppenderAttachablePtr aa = 
LOG4CXX_NS::cast<AppenderAttachable>(appender);
-                                       if (auto delegateAppender = 
findAppenderByReference(p, utf8Decoder, currentElement, doc, appenders))
+                                       if (auto delegateAppender = 
findAppenderByReference(currentElement))
                                        {
                                                if (LogLog::isDebugEnabled())
                                                {
@@ -344,9 +390,19 @@ AppenderPtr DOMConfigurator::parseAppender(Pool& p,
                                                + LOG4CXX_STR(" which does not 
implement ") + AppenderAttachable::getStaticClass().getName());
                                }
                        }
+                       else
+                       {
+                               LogString msg{ LOG4CXX_STR("Ignoring unknown 
[") };
+                               utf8Decoder->decode(currentElement->name, 
MAX_ATTRIBUTE_NAME_LEN, msg);
+                               msg += LOG4CXX_STR("] ");
+                               utf8Decoder->decode(appenderElement->name, 
MAX_ATTRIBUTE_NAME_LEN, msg);
+                               msg += LOG4CXX_STR(" element");
+                               LogLog::warn(msg);
+                       }
                }
 
                propSetter.activate(p);
+               appenders[appender->getName()].activated = true;
                return appender;
        }
        /* Yes, it's ugly.  But all of these exceptions point to the same
@@ -361,17 +417,12 @@ AppenderPtr DOMConfigurator::parseAppender(Pool& p,
 /**
 Used internally to parse an {@link ErrorHandler} element.
 */
-void DOMConfigurator::parseErrorHandler(Pool& p,
-       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
-       apr_xml_elem* element,
-       AppenderPtr& appender,
-       apr_xml_doc* doc,
-       AppenderMap& appenders)
+void DOMConfigurator::DOMConfiguratorPrivate::parseErrorHandler(apr_xml_elem* 
element, const AppenderPtr& appender)
 {
 
        ErrorHandlerPtr eh;
        std::shared_ptr<Object> obj = OptionConverter::instantiateByClassName(
-                       subst(getAttribute(utf8Decoder, element, CLASS_ATTR)),
+                       subst(getAttribute(element, CLASS_ATTR)),
                        ErrorHandler::getStaticClass(),
                        0);
        eh = LOG4CXX_NS::cast<ErrorHandler>(obj);
@@ -390,45 +441,48 @@ void DOMConfigurator::parseErrorHandler(Pool& p,
 
                        if (tagName == PARAM_TAG)
                        {
-                               setParameter(p, utf8Decoder, currentElement, 
propSetter);
+                               setParameter(currentElement, propSetter);
                        }
                        else if (tagName == APPENDER_REF_TAG)
                        {
-                               if (auto appender = findAppenderByReference(p, 
utf8Decoder, currentElement, doc, appenders))
+                               if (auto appender = 
findAppenderByReference(currentElement))
                                        eh->setBackupAppender(appender);
                        }
                        else if (tagName == LOGGER_REF)
                        {
-                               LogString loggerName(getAttribute(utf8Decoder, 
currentElement, REF_ATTR));
-                               LoggerPtr logger = 
m_priv->repository->getLogger(loggerName, m_priv->loggerFactory);
+                               LogString 
loggerName(getAttribute(currentElement, REF_ATTR));
+                               LoggerPtr logger = 
this->repository->getLogger(loggerName, this->loggerFactory);
                                eh->setLogger(logger);
                        }
                        else if (tagName == ROOT_REF)
                        {
-                               LoggerPtr root = 
m_priv->repository->getRootLogger();
+                               LoggerPtr root = 
this->repository->getRootLogger();
                                eh->setLogger(root);
                        }
+                       else
+                       {
+                               LogString msg{ LOG4CXX_STR("Ignoring unknown 
[") };
+                               utf8Decoder->decode(currentElement->name, 
MAX_ATTRIBUTE_NAME_LEN, msg);
+                               msg += LOG4CXX_STR("] ");
+                               utf8Decoder->decode(element->name, 
MAX_ATTRIBUTE_NAME_LEN, msg);
+                               msg += LOG4CXX_STR(" element");
+                               LogLog::warn(msg);
+                       }
                }
 
                propSetter.activate(p);
-               std::shared_ptr<AppenderSkeleton> appSkeleton = 
LOG4CXX_NS::cast<AppenderSkeleton>(appender);
-
-               if (appSkeleton != 0)
-               {
+               if (auto appSkeleton = 
LOG4CXX_NS::cast<AppenderSkeleton>(appender))
                        appSkeleton->setErrorHandler(eh);
-               }
        }
 }
 
 /**
  Used internally to parse a filter element.
 */
-void DOMConfigurator::parseFilters(Pool& p,
-       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
-       apr_xml_elem* element,
-       std::vector<LOG4CXX_NS::spi::FilterPtr>& filters)
+FilterStore 
DOMConfigurator::DOMConfiguratorPrivate::parseFilters(apr_xml_elem* element)
 {
-       LogString clazz = subst(getAttribute(utf8Decoder, element, CLASS_ATTR));
+       FilterStore result;
+       LogString clazz = subst(getAttribute(element, CLASS_ATTR));
        FilterPtr filter;
        std::shared_ptr<Object> obj = 
OptionConverter::instantiateByClassName(clazz,
                        Filter::getStaticClass(), 0);
@@ -446,39 +500,44 @@ void DOMConfigurator::parseFilters(Pool& p,
 
                        if (tagName == PARAM_TAG)
                        {
-                               setParameter(p, utf8Decoder, currentElement, 
propSetter);
+                               setParameter(currentElement, propSetter);
+                       }
+                       else
+                       {
+                               LogString msg{ LOG4CXX_STR("Ignoring unknown 
[") };
+                               utf8Decoder->decode(currentElement->name, 
MAX_ATTRIBUTE_NAME_LEN, msg);
+                               msg += LOG4CXX_STR("] ");
+                               utf8Decoder->decode(element->name, 
MAX_ATTRIBUTE_NAME_LEN, msg);
+                               msg += LOG4CXX_STR(" element");
+                               LogLog::warn(msg);
                        }
                }
 
                propSetter.activate(p);
-               filters.push_back(filter);
+               result.push_back(filter);
        }
+       return result;
 }
 
 /**
 Used internally to parse an category or logger element.
 */
-void DOMConfigurator::parseLogger(
-       LOG4CXX_NS::helpers::Pool& p,
-       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
-       apr_xml_elem* loggerElement,
-       apr_xml_doc* doc,
-       AppenderMap& appenders)
+void DOMConfigurator::DOMConfiguratorPrivate::parseLogger(apr_xml_elem* 
loggerElement)
 {
        // Create a new Logger object from the <category> element.
-       LogString loggerName = subst(getAttribute(utf8Decoder, loggerElement, 
NAME_ATTR));
+       LogString loggerName = subst(getAttribute(loggerElement, NAME_ATTR));
 
        if (LogLog::isDebugEnabled())
        {
                LogLog::debug(LOG4CXX_STR("Getting [") + loggerName + 
LOG4CXX_STR("]"));
        }
-       LoggerPtr logger = m_priv->repository->getLogger(loggerName, 
m_priv->loggerFactory);
+       LoggerPtr logger = this->repository->getLogger(loggerName, 
this->loggerFactory);
 
        // Setting up a logger needs to be an atomic operation, in order
        // to protect potential log operations while logger
        // configuration is in progress.
        bool additivity = OptionConverter::toBoolean(
-                       subst(getAttribute(utf8Decoder, loggerElement, 
ADDITIVITY_ATTR)),
+                       subst(getAttribute(loggerElement, ADDITIVITY_ATTR)),
                        true);
 
        if (LogLog::isDebugEnabled())
@@ -487,18 +546,15 @@ void DOMConfigurator::parseLogger(
                        (additivity ? LogString(LOG4CXX_STR("true")) : 
LogString(LOG4CXX_STR("false"))) + LOG4CXX_STR("]"));
        }
        logger->setAdditivity(additivity);
-       parseChildrenOfLoggerElement(p, utf8Decoder, loggerElement, logger, 
false, doc, appenders);
+       parseChildrenOfLoggerElement(loggerElement, logger, false);
 }
 
 /**
  Used internally to parse the logger factory element.
 */
-void DOMConfigurator::parseLoggerFactory(
-       LOG4CXX_NS::helpers::Pool& p,
-       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
-       apr_xml_elem* factoryElement)
+void DOMConfigurator::DOMConfiguratorPrivate::parseLoggerFactory(apr_xml_elem* 
factoryElement)
 {
-       LogString className(subst(getAttribute(utf8Decoder, factoryElement, 
CLASS_ATTR)));
+       LogString className(subst(getAttribute(factoryElement, CLASS_ATTR)));
 
        if (className.empty())
        {
@@ -520,8 +576,8 @@ void DOMConfigurator::parseLoggerFactory(
                        , std::make_shared<LoggerFactory>()
 #endif
                        );
-               m_priv->loggerFactory = LOG4CXX_NS::cast<LoggerFactory>(obj);
-               PropertySetter propSetter(m_priv->loggerFactory);
+               this->loggerFactory = LOG4CXX_NS::cast<LoggerFactory>(obj);
+               PropertySetter propSetter(this->loggerFactory);
 
                for (apr_xml_elem* currentElement = factoryElement->first_child;
                        currentElement;
@@ -531,7 +587,7 @@ void DOMConfigurator::parseLoggerFactory(
 
                        if (tagName == PARAM_TAG)
                        {
-                               setParameter(p, utf8Decoder, currentElement, 
propSetter);
+                               setParameter(currentElement, propSetter);
                        }
                }
        }
@@ -540,33 +596,23 @@ void DOMConfigurator::parseLoggerFactory(
 /**
  Used internally to parse the root logger element.
 */
-void DOMConfigurator::parseRoot(
-       LOG4CXX_NS::helpers::Pool& p,
-       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
-       apr_xml_elem* rootElement,
-       apr_xml_doc* doc,
-       AppenderMap& appenders)
+void DOMConfigurator::DOMConfiguratorPrivate::parseRoot(apr_xml_elem* 
rootElement)
 {
-       LoggerPtr root = m_priv->repository->getRootLogger();
-       parseChildrenOfLoggerElement(p, utf8Decoder, rootElement, root, true, 
doc, appenders);
+       LoggerPtr root = this->repository->getRootLogger();
+       parseChildrenOfLoggerElement(rootElement, root, true);
 }
 
 /**
  Used internally to parse the children of a logger element.
 */
-void DOMConfigurator::parseChildrenOfLoggerElement(
-       LOG4CXX_NS::helpers::Pool& p,
-       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
-       apr_xml_elem* loggerElement, LoggerPtr logger, bool isRoot,
-       apr_xml_doc* doc,
-       AppenderMap& appenders)
+void 
DOMConfigurator::DOMConfiguratorPrivate::parseChildrenOfLoggerElement(apr_xml_elem*
 loggerElement, LoggerPtr logger, bool isRoot)
 {
        PropertySetter propSetter(logger);
-       auto loggerName = m_priv->repository->getRootLogger() == logger
+       auto loggerName = this->repository->getRootLogger() == logger
                                        ? LogString(LOG4CXX_STR("root"))
                                        : logger->getName();
        AsyncAppenderPtr async;
-       auto lsAsynchronous = subst(getAttribute(utf8Decoder, loggerElement, 
ASYNCHRONOUS_ATTR));
+       auto lsAsynchronous = subst(getAttribute(loggerElement, 
ASYNCHRONOUS_ATTR));
        if (!lsAsynchronous.empty() && 
OptionConverter::toBoolean(lsAsynchronous, true))
        {
                async = std::make_shared<AsyncAppender>();
@@ -582,7 +628,7 @@ void DOMConfigurator::parseChildrenOfLoggerElement(
 
                if (tagName == APPENDER_REF_TAG)
                {
-                       if (auto appender = findAppenderByReference(p, 
utf8Decoder, currentElement, doc, appenders))
+                       if (auto appender = 
findAppenderByReference(currentElement))
                        {
                                if (log4cxx::cast<AsyncAppender>(appender)) // 
An explicitly configured AsyncAppender?
                                        async.reset(); // Not required
@@ -599,15 +645,24 @@ void DOMConfigurator::parseChildrenOfLoggerElement(
                }
                else if (tagName == LEVEL_TAG)
                {
-                       parseLevel(p, utf8Decoder, currentElement, logger, 
isRoot);
+                       parseLevel(currentElement, logger, isRoot);
                }
                else if (tagName == PRIORITY_TAG)
                {
-                       parseLevel(p, utf8Decoder, currentElement, logger, 
isRoot);
+                       parseLevel(currentElement, logger, isRoot);
                }
                else if (tagName == PARAM_TAG)
                {
-                       setParameter(p, utf8Decoder, currentElement, 
propSetter);
+                       setParameter(currentElement, propSetter);
+               }
+               else
+               {
+                       LogString msg{ LOG4CXX_STR("Ignoring unknown [") };
+                       utf8Decoder->decode(currentElement->name, 
MAX_ATTRIBUTE_NAME_LEN, msg);
+                       msg += LOG4CXX_STR("] ");
+                       utf8Decoder->decode(loggerElement->name, 
MAX_ATTRIBUTE_NAME_LEN, msg);
+                       msg += LOG4CXX_STR(" element");
+                       LogLog::warn(msg);
                }
        }
        if (async && !newappenders.empty())
@@ -618,14 +673,14 @@ void DOMConfigurator::parseChildrenOfLoggerElement(
                                        + logger->getName() + LOG4CXX_STR("] is 
on"));
                }
                logger->replaceAppenders({async});
-               m_priv->appenderAdded = true;
+               this->appenderAdded = true;
        }
        else if (newappenders.empty())
                logger->removeAllAppenders();
        else
        {
                logger->replaceAppenders(newappenders);
-               m_priv->appenderAdded = true;
+               this->appenderAdded = true;
        }
        propSetter.activate(p);
 }
@@ -633,12 +688,9 @@ void DOMConfigurator::parseChildrenOfLoggerElement(
 /**
  Used internally to parse a layout element.
 */
-LayoutPtr DOMConfigurator::parseLayout (
-       LOG4CXX_NS::helpers::Pool& p,
-       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
-       apr_xml_elem* layout_element)
+LayoutPtr DOMConfigurator::DOMConfiguratorPrivate::parseLayout(apr_xml_elem* 
layout_element)
 {
-       LogString className(subst(getAttribute(utf8Decoder, layout_element, 
CLASS_ATTR)));
+       LogString className(subst(getAttribute(layout_element, CLASS_ATTR)));
        if (LogLog::isDebugEnabled())
        {
                LogLog::debug(LOG4CXX_STR("Desired ") + 
Layout::getStaticClass().getName()
@@ -659,7 +711,16 @@ LayoutPtr DOMConfigurator::parseLayout (
 
                        if (tagName == PARAM_TAG)
                        {
-                               setParameter(p, utf8Decoder, currentElement, 
propSetter);
+                               setParameter(currentElement, propSetter);
+                       }
+                       else
+                       {
+                               LogString msg{ LOG4CXX_STR("Ignoring unknown 
[") };
+                               utf8Decoder->decode(currentElement->name, 
MAX_ATTRIBUTE_NAME_LEN, msg);
+                               msg += LOG4CXX_STR("] ");
+                               utf8Decoder->decode(layout_element->name, 
MAX_ATTRIBUTE_NAME_LEN, msg);
+                               msg += LOG4CXX_STR(" element");
+                               LogLog::warn(msg);
                        }
                }
 
@@ -676,12 +737,9 @@ LayoutPtr DOMConfigurator::parseLayout (
 /**
  Used internally to parse a triggering policy
 */
-ObjectPtr DOMConfigurator::parseTriggeringPolicy (
-       LOG4CXX_NS::helpers::Pool& p,
-       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
-       apr_xml_elem* policy_element)
+ObjectPtr 
DOMConfigurator::DOMConfiguratorPrivate::parseTriggeringPolicy(apr_xml_elem* 
policy_element)
 {
-       LogString className = subst(getAttribute(utf8Decoder, policy_element, 
CLASS_ATTR));
+       LogString className = subst(getAttribute(policy_element, CLASS_ATTR));
        if (LogLog::isDebugEnabled())
        {
                LogLog::debug(LOG4CXX_STR("Desired ") + 
TriggeringPolicy::getStaticClass().getName()
@@ -701,15 +759,12 @@ ObjectPtr DOMConfigurator::parseTriggeringPolicy (
 
                        if (tagName == PARAM_TAG)
                        {
-                               setParameter(p, utf8Decoder, currentElement, 
propSetter);
+                               setParameter(currentElement, propSetter);
                        }
                        else if (tagName == FILTER_TAG)
                        {
-                               std::vector<LOG4CXX_NS::spi::FilterPtr> filters;
-                               parseFilters(p, utf8Decoder, currentElement, 
filters);
-                               FilterBasedTriggeringPolicyPtr fbtp = 
LOG4CXX_NS::cast<FilterBasedTriggeringPolicy>(instance);
-
-                               if (fbtp != NULL)
+                               auto filters = parseFilters(currentElement);
+                               if (auto fbtp = 
LOG4CXX_NS::cast<FilterBasedTriggeringPolicy>(instance))
                                {
                                        for (auto& item : filters)
                                        {
@@ -717,6 +772,15 @@ ObjectPtr DOMConfigurator::parseTriggeringPolicy (
                                        }
                                }
                        }
+                       else
+                       {
+                               LogString msg{ LOG4CXX_STR("Ignoring unknown 
[") };
+                               utf8Decoder->decode(currentElement->name, 
MAX_ATTRIBUTE_NAME_LEN, msg);
+                               msg += LOG4CXX_STR("] ");
+                               utf8Decoder->decode(policy_element->name, 
MAX_ATTRIBUTE_NAME_LEN, msg);
+                               msg += LOG4CXX_STR(" element");
+                               LogLog::warn(msg);
+                       }
                }
 
                propSetter.activate(p);
@@ -732,12 +796,9 @@ ObjectPtr DOMConfigurator::parseTriggeringPolicy (
 /**
  Used internally to parse a triggering policy
 */
-RollingPolicyPtr DOMConfigurator::parseRollingPolicy (
-       LOG4CXX_NS::helpers::Pool& p,
-       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
-       apr_xml_elem* policy_element)
+RollingPolicyPtr 
DOMConfigurator::DOMConfiguratorPrivate::parseRollingPolicy(apr_xml_elem* 
policy_element)
 {
-       LogString className = subst(getAttribute(utf8Decoder, policy_element, 
CLASS_ATTR));
+       LogString className = subst(getAttribute(policy_element, CLASS_ATTR));
        if (LogLog::isDebugEnabled())
        {
                LogLog::debug(LOG4CXX_STR("Desired ") + 
RollingPolicy::getStaticClass().getName()
@@ -757,7 +818,16 @@ RollingPolicyPtr DOMConfigurator::parseRollingPolicy (
 
                        if (tagName == PARAM_TAG)
                        {
-                               setParameter(p, utf8Decoder, currentElement, 
propSetter);
+                               setParameter(currentElement, propSetter);
+                       }
+                       else
+                       {
+                               LogString msg{ LOG4CXX_STR("Ignoring unknown 
[") };
+                               utf8Decoder->decode(currentElement->name, 
MAX_ATTRIBUTE_NAME_LEN, msg);
+                               msg += LOG4CXX_STR("] ");
+                               utf8Decoder->decode(policy_element->name, 
MAX_ATTRIBUTE_NAME_LEN, msg);
+                               msg += LOG4CXX_STR(" element");
+                               LogLog::warn(msg);
                        }
                }
 
@@ -776,10 +846,7 @@ RollingPolicyPtr DOMConfigurator::parseRollingPolicy (
 /**
  Used internally to parse a level  element.
 */
-void DOMConfigurator::parseLevel(
-       LOG4CXX_NS::helpers::Pool& p,
-       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
-       apr_xml_elem* element, LoggerPtr logger, bool isRoot)
+void DOMConfigurator::DOMConfiguratorPrivate::parseLevel(apr_xml_elem* 
element, LoggerPtr logger, bool isRoot)
 {
        LogString loggerName = logger->getName();
 
@@ -788,7 +855,7 @@ void DOMConfigurator::parseLevel(
                loggerName = LOG4CXX_STR("root");
        }
 
-       LogString levelStr(subst(getAttribute(utf8Decoder, element, 
VALUE_ATTR)));
+       LogString levelStr(subst(getAttribute(element, VALUE_ATTR)));
        if (LogLog::isDebugEnabled())
        {
                LogLog::debug(LOG4CXX_STR("Setting [") + loggerName + 
LOG4CXX_STR("] level to [") + levelStr + LOG4CXX_STR("]"));
@@ -808,7 +875,7 @@ void DOMConfigurator::parseLevel(
        }
        else
        {
-               LogString className(subst(getAttribute(utf8Decoder, element, 
CLASS_ATTR)));
+               LogString className(subst(getAttribute(element, CLASS_ATTR)));
 
                if (className.empty())
                {
@@ -848,13 +915,10 @@ void DOMConfigurator::parseLevel(
        }
 }
 
-void DOMConfigurator::setParameter(LOG4CXX_NS::helpers::Pool& p,
-       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
-       apr_xml_elem* elem,
-       PropertySetter& propSetter)
+void DOMConfigurator::DOMConfiguratorPrivate::setParameter(apr_xml_elem* elem, 
PropertySetter& propSetter)
 {
-       LogString name(subst(getAttribute(utf8Decoder, elem, NAME_ATTR)));
-       LogString value(subst(getAttribute(utf8Decoder, elem, VALUE_ATTR)));
+       LogString name(subst(getAttribute(elem, NAME_ATTR)));
+       LogString value(subst(getAttribute(elem, VALUE_ATTR)));
        value = subst(value);
        propSetter.setProperty(name, value, p);
 }
@@ -868,18 +932,12 @@ spi::ConfigurationStatus DOMConfigurator::doConfigure
 #endif
        )
 {
-       m_priv->repository = repository ? repository : 
LogManager::getLoggerRepository();
+       m_priv = std::make_unique<DOMConfiguratorPrivate>
+               ( repository ? repository : LogManager::getLoggerRepository()
+               );
 
-#if LOG4CXX_ABI_VERSION <= 15
-       m_priv->loggerFactory = std::make_shared<DefaultLoggerFactory>();
-#else
-       m_priv->loggerFactory = std::make_shared<LoggerFactory>();
-#endif
-
-       Pool p;
        apr_file_t* fd;
-
-       log4cxx_status_t rv = filename.open(&fd, APR_READ, APR_OS_DEFAULT, p);
+       log4cxx_status_t rv = filename.open(&fd, APR_READ, APR_OS_DEFAULT, 
m_priv->p);
 
        if (rv != APR_SUCCESS)
        {
@@ -890,16 +948,14 @@ spi::ConfigurationStatus DOMConfigurator::doConfigure
        }
        else
        {
-               apr_xml_parser* parser = NULL;
-               apr_xml_doc* doc = NULL;
-
                if (LogLog::isDebugEnabled())
                {
                        LogLog::debug(LOG4CXX_STR("Loading configuration file 
[")
                                        + filename.getPath() + 
LOG4CXX_STR("]"));
                }
 
-               rv = apr_xml_parse_file(p.getAPRPool(), &parser, &doc, fd, 
2000);
+               apr_xml_parser* parser = NULL;
+               rv = apr_xml_parse_file(m_priv->p.getAPRPool(), &parser, 
&m_priv->doc, fd, 2000);
 
                if (rv != APR_SUCCESS)
                {
@@ -925,9 +981,7 @@ spi::ConfigurationStatus DOMConfigurator::doConfigure
                }
                else
                {
-                       AppenderMap appenders;
-                       CharsetDecoderPtr 
utf8Decoder(CharsetDecoder::getUTF8Decoder());
-                       parse(p, utf8Decoder, doc->root, doc, appenders);
+                       m_priv->parse(m_priv->doc->root);
                }
        }
 
@@ -1052,12 +1106,7 @@ spi::ConfigurationStatus 
DOMConfigurator::configureAndWatch(const CFStringRef& f
 #endif
 #endif // LOG4CXX_ABI_VERSION <= 15
 
-void DOMConfigurator::parse(
-       Pool& p,
-       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
-       apr_xml_elem* element,
-       apr_xml_doc* doc,
-       AppenderMap& appenders)
+void DOMConfigurator::DOMConfiguratorPrivate::parse(apr_xml_elem* element)
 {
        std::string rootElementName(element->name);
 
@@ -1082,7 +1131,7 @@ void DOMConfigurator::parse(
                }
        }
 
-       LogString debugAttrib = subst(getAttribute(utf8Decoder, element, 
INTERNAL_DEBUG_ATTR));
+       LogString debugAttrib = subst(getAttribute(element, 
INTERNAL_DEBUG_ATTR));
 
        // if the log4j.dtd is not specified in the XML file, then the
        // "debug" attribute is returned as the empty string.
@@ -1091,26 +1140,26 @@ void DOMConfigurator::parse(
                
LogLog::setInternalDebugging(OptionConverter::toBoolean(debugAttrib, true));
        }
 
-       LogString colorAttrib = subst(getAttribute(utf8Decoder, element, 
INTERNAL_COLOR_ATTR));
+       LogString colorAttrib = subst(getAttribute(element, 
INTERNAL_COLOR_ATTR));
        if (!colorAttrib.empty())
        {
                LogLog::setColorEnabled(OptionConverter::toBoolean(colorAttrib, 
true));
        }
 
-       LogString thresholdStr = subst(getAttribute(utf8Decoder, element, 
THRESHOLD_ATTR));
+       LogString thresholdStr = subst(getAttribute(element, THRESHOLD_ATTR));
 
        if (!thresholdStr.empty() && thresholdStr != LOG4CXX_STR("NULL"))
        {
-               
m_priv->repository->setThreshold(OptionConverter::toLevel(thresholdStr, 
Level::getAll()));
+               
this->repository->setThreshold(OptionConverter::toLevel(thresholdStr, 
Level::getAll()));
                if (LogLog::isDebugEnabled())
                {
                        LogLog::debug(LOG4CXX_STR("Repository threshold =[")
-                               + m_priv->repository->getThreshold()->toString()
+                               + this->repository->getThreshold()->toString()
                                + LOG4CXX_STR("]"));
                }
        }
 
-       LogString threadSignalValue = subst(getAttribute(utf8Decoder, element, 
THREAD_CONFIG_ATTR));
+       LogString threadSignalValue = subst(getAttribute(element, 
THREAD_CONFIG_ATTR));
 
        if ( !threadSignalValue.empty() && threadSignalValue != 
LOG4CXX_STR("NULL") )
        {
@@ -1150,7 +1199,7 @@ void DOMConfigurator::parse(
 
                if (tagName == CATEGORY_FACTORY_TAG)
                {
-                       parseLoggerFactory(p, utf8Decoder, currentElement);
+                       parseLoggerFactory(currentElement);
                }
        }
 
@@ -1162,20 +1211,20 @@ void DOMConfigurator::parse(
 
                if (tagName == CATEGORY || tagName == LOGGER)
                {
-                       parseLogger(p, utf8Decoder, currentElement, doc, 
appenders);
+                       parseLogger(currentElement);
                }
                else if (tagName == ROOT_TAG)
                {
-                       parseRoot(p, utf8Decoder, currentElement, doc, 
appenders);
+                       parseRoot(currentElement);
                }
        }
 }
 
-LogString DOMConfigurator::subst(const LogString& value)
+LogString DOMConfigurator::DOMConfiguratorPrivate::subst(const LogString& 
value)
 {
        try
        {
-               return OptionConverter::substVars(value, m_priv->props);
+               return OptionConverter::substVars(value, this->props);
        }
        catch (IllegalArgumentException& e)
        {
@@ -1185,10 +1234,7 @@ LogString DOMConfigurator::subst(const LogString& value)
 }
 
 
-LogString DOMConfigurator::getAttribute(
-       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
-       apr_xml_elem* element,
-       const std::string& attrName)
+LogString DOMConfigurator::DOMConfiguratorPrivate::getAttribute(apr_xml_elem* 
element, const std::string& attrName)
 {
        LogString attrValue;
 
@@ -1204,3 +1250,103 @@ LogString DOMConfigurator::getAttribute(
 
        return attrValue;
 }
+
+#if LOG4CXX_ABI_VERSION <= 15
+AppenderPtr DOMConfigurator::findAppenderByName(LOG4CXX_NS::helpers::Pool& p,
+       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
+       apr_xml_elem* element,
+       apr_xml_doc* doc,
+       const LogString& appenderName,
+       AppenderMap& appenders)
+{ return AppenderPtr{}; }
+AppenderPtr DOMConfigurator::findAppenderByReference(
+       LOG4CXX_NS::helpers::Pool& p,
+       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
+       apr_xml_elem* appenderRef,
+       apr_xml_doc* doc,
+       AppenderMap& appenders)
+{ return AppenderPtr{}; }
+AppenderPtr DOMConfigurator::parseAppender(Pool& p,
+       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
+       apr_xml_elem* appenderElement,
+       apr_xml_doc* doc,
+       AppenderMap& appenders)
+{ return AppenderPtr{}; }
+void DOMConfigurator::parseErrorHandler(Pool& p,
+       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
+       apr_xml_elem* element,
+       AppenderPtr& appender,
+       apr_xml_doc* doc,
+       AppenderMap& appenders)
+{}
+void DOMConfigurator::parseFilters(Pool& p,
+       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
+       apr_xml_elem* element,
+       std::vector<LOG4CXX_NS::spi::FilterPtr>& filters)
+{}
+void DOMConfigurator::parseLogger(
+       LOG4CXX_NS::helpers::Pool& p,
+       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
+       apr_xml_elem* loggerElement,
+       apr_xml_doc* doc,
+       AppenderMap& appenders)
+{}
+void DOMConfigurator::parseLoggerFactory(
+       LOG4CXX_NS::helpers::Pool& p,
+       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
+       apr_xml_elem* factoryElement)
+{}
+void DOMConfigurator::parseRoot(
+       LOG4CXX_NS::helpers::Pool& p,
+       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
+       apr_xml_elem* rootElement,
+       apr_xml_doc* doc,
+       AppenderMap& appenders)
+{}
+void DOMConfigurator::parseChildrenOfLoggerElement(
+       LOG4CXX_NS::helpers::Pool& p,
+       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
+       apr_xml_elem* loggerElement, LoggerPtr logger, bool isRoot,
+       apr_xml_doc* doc,
+       AppenderMap& appenders)
+{}
+LayoutPtr DOMConfigurator::parseLayout (
+       LOG4CXX_NS::helpers::Pool& p,
+       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
+       apr_xml_elem* layout_element)
+{ return LayoutPtr{}; }
+ObjectPtr DOMConfigurator::parseTriggeringPolicy (
+       LOG4CXX_NS::helpers::Pool& p,
+       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
+       apr_xml_elem* policy_element)
+{ return ObjectPtr{}; }
+RollingPolicyPtr DOMConfigurator::parseRollingPolicy (
+       LOG4CXX_NS::helpers::Pool& p,
+       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
+       apr_xml_elem* policy_element)
+{ return RollingPolicyPtr{}; }
+void DOMConfigurator::parseLevel(
+       LOG4CXX_NS::helpers::Pool& p,
+       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
+       apr_xml_elem* element, LoggerPtr logger, bool isRoot)
+{}
+void DOMConfigurator::setParameter(LOG4CXX_NS::helpers::Pool& p,
+       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
+       apr_xml_elem* elem,
+       PropertySetter& propSetter)
+{}
+void DOMConfigurator::parse(
+       Pool& p,
+       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
+       apr_xml_elem* element,
+       apr_xml_doc* doc,
+       AppenderMap& appenders)
+{}
+LogString DOMConfigurator::getAttribute(
+       LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
+       apr_xml_elem* element,
+       const std::string& attrName)
+{ return LogString{}; }
+LogString DOMConfigurator::subst(const LogString& value)
+{ return LogString{}; }
+#endif
\ No newline at end of file
diff --git a/src/main/include/log4cxx/xml/domconfigurator.h 
b/src/main/include/log4cxx/xml/domconfigurator.h
index 16ae64b7..e28d9824 100644
--- a/src/main/include/log4cxx/xml/domconfigurator.h
+++ b/src/main/include/log4cxx/xml/domconfigurator.h
@@ -19,18 +19,20 @@
 #define _LOG4CXX_XML_DOM_CONFIGURATOR_H
 
 #include <log4cxx/logstring.h>
-#include <map>
+#include <log4cxx/spi/configurator.h>
+#if LOG4CXX_ABI_VERSION <= 15
 #include <log4cxx/appender.h>
 #include <log4cxx/layout.h>
 #include <log4cxx/logger.h>
 #include <log4cxx/helpers/properties.h>
-#include <log4cxx/spi/configurator.h>
 #include <log4cxx/helpers/charsetdecoder.h>
 #include <log4cxx/spi/filter.h>
 #include <log4cxx/rolling/triggeringpolicy.h>
 #include <log4cxx/rolling/rollingpolicy.h>
-#include <log4cxx/file.h>
 #include <log4cxx/config/propertysetter.h>
+#include <map>
+#endif
+#include <log4cxx/file.h>
 
 #if LOG4CXX_HAS_DOMCONFIGURATOR
 
@@ -67,6 +69,7 @@ class LOG4CXX_EXPORT DOMConfigurator :
        public:
                ~DOMConfigurator();
 
+#if LOG4CXX_ABI_VERSION <= 15
        protected:
                typedef std::map<LogString, AppenderPtr> AppenderMap;
                /**
@@ -205,6 +208,7 @@ class LOG4CXX_EXPORT DOMConfigurator :
                        apr_xml_elem* element,
                        apr_xml_doc* doc,
                        AppenderMap& appenders);
+#endif // LOG4CXX_ABI_VERSION <= 15
 
        public:
                DOMConfigurator();
@@ -214,9 +218,9 @@ class LOG4CXX_EXPORT DOMConfigurator :
                LOG4CXX_CAST_ENTRY(spi::Configurator)
                END_LOG4CXX_CAST_MAP()
 
+#if LOG4CXX_ABI_VERSION <= 15
                DOMConfigurator(LOG4CXX_NS::helpers::Pool& p);
 
-#if LOG4CXX_ABI_VERSION <= 15
                /**
                A static version of #doConfigure.
                */
@@ -362,6 +366,7 @@ class LOG4CXX_EXPORT DOMConfigurator :
                */
                static spi::ConfigurationStatus configureAndWatch(const File& 
configFilename, long delay = 0);
 
+#if LOG4CXX_ABI_VERSION <= 15
        protected:
                static LogString getAttribute(
                        LOG4CXX_NS::helpers::CharsetDecoderPtr& utf8Decoder,
@@ -369,6 +374,7 @@ class LOG4CXX_EXPORT DOMConfigurator :
                        const std::string& attrName);
 
                LogString subst(const LogString& value);
+#endif
 
        private:
                //   prevent assignment or copy statements
diff --git a/src/test/cpp/asyncappendertestcase.cpp 
b/src/test/cpp/asyncappendertestcase.cpp
index 2c474528..276d6705 100644
--- a/src/test/cpp/asyncappendertestcase.cpp
+++ b/src/test/cpp/asyncappendertestcase.cpp
@@ -48,7 +48,7 @@ struct MyStruct
 {
        int value;
 };
-using OutputStreamType = std::basic_ostream<log4cxx::logchar>;
+using OutputStreamType = std::basic_ostream<LOG4CXX_NS::logchar>;
 OutputStreamType& operator<<(OutputStreamType& stream, const MyStruct& data)
 {
        stream << LOG4CXX_STR("[MyStruct value: ") << data.value << 
LOG4CXX_STR("]");
@@ -56,13 +56,13 @@ OutputStreamType& operator<<(OutputStreamType& stream, 
const MyStruct& data)
 }
 
 #if FMT_VERSION >= (9 * 10000)
-template <> struct fmt::formatter<MyStruct, log4cxx::logchar> : 
fmt::basic_ostream_formatter<log4cxx::logchar> {};
+template <> struct fmt::formatter<MyStruct, LOG4CXX_NS::logchar> : 
fmt::basic_ostream_formatter<LOG4CXX_NS::logchar> {};
 #endif
 #endif
 
-using namespace log4cxx;
-using namespace log4cxx::helpers;
-using namespace log4cxx::spi;
+using namespace LOG4CXX_NS;
+using namespace LOG4CXX_NS::helpers;
+using namespace LOG4CXX_NS::spi;
 
 class NullPointerAppender : public AppenderSkeleton
 {
@@ -75,7 +75,7 @@ class NullPointerAppender : public AppenderSkeleton
                /**
                 * @{inheritDoc}
                 */
-               void append(const spi::LoggingEventPtr&, 
log4cxx::helpers::Pool&) override
+               void append(const spi::LoggingEventPtr&, 
LOG4CXX_NS::helpers::Pool&) override
                {
                        throw RuntimeException(LOG4CXX_STR("Intentional 
Exception"));
                }
@@ -108,7 +108,7 @@ class BlockableVectorAppender : public VectorAppender
                /**
                 * {@inheritDoc}
                 */
-               void append(const spi::LoggingEventPtr& event, 
log4cxx::helpers::Pool& p) override
+               void append(const spi::LoggingEventPtr& event, 
LOG4CXX_NS::helpers::Pool& p) override
                {
                        std::lock_guard<std::mutex> lock( blocker );
                        VectorAppender::append(event, p);
@@ -128,7 +128,7 @@ LOG4CXX_PTR_DEF(BlockableVectorAppender);
 class LoggingVectorAppender : public VectorAppender
 {
        LoggerInstancePtr logger{ "LoggingVectorAppender" };
-       void append(const spi::LoggingEventPtr& event, log4cxx::helpers::Pool& 
p) override
+       void append(const spi::LoggingEventPtr& event, 
LOG4CXX_NS::helpers::Pool& p) override
        {
                auto& lsMsg = event->getRenderedMessage();
                VectorAppender::append(event, p);
@@ -165,6 +165,7 @@ class AsyncAppenderTestCase : public 
AppenderSkeletonTestCase
 #if LOG4CXX_HAS_DOMCONFIGURATOR
                LOGUNIT_TEST(testXMLConfiguration);
                LOGUNIT_TEST(testAsyncLoggerXML);
+               LOGUNIT_TEST(testRecursiveConfiguration);
 #endif
                LOGUNIT_TEST(testAsyncLoggerProperties);
 #if LOG4CXX_ASYNC_BUFFER_SUPPORTS_FMT
@@ -503,7 +504,7 @@ class AsyncAppenderTestCase : public 
AppenderSkeletonTestCase
                        LOGUNIT_ASSERT_EQUAL(levelCount[Level::getError()], 1);
                        LOGUNIT_ASSERT_EQUAL(discardEvent->getLevel(), 
Level::getError());
                        // Check the discard message does not have location info
-                       
LOGUNIT_ASSERT_EQUAL(log4cxx::spi::LocationInfo::getLocationUnavailable().getClassName(),
+                       
LOGUNIT_ASSERT_EQUAL(LOG4CXX_NS::spi::LocationInfo::getLocationUnavailable().getClassName(),
                                
discardEvent->getLocationInformation().getClassName());
                }
 
@@ -585,7 +586,7 @@ class AsyncAppenderTestCase : public 
AppenderSkeletonTestCase
 
                        // Check configuration is as expected
                        auto  root = Logger::getRootLogger();
-                       auto asyncAppender = 
log4cxx::cast<AsyncAppender>(root->getAppender(LOG4CXX_STR("ASYNC")));
+                       auto asyncAppender = 
LOG4CXX_NS::cast<AsyncAppender>(root->getAppender(LOG4CXX_STR("ASYNC")));
                        LOGUNIT_ASSERT(asyncAppender);
                        LOGUNIT_ASSERT_EQUAL(100, 
asyncAppender->getBufferSize());
                        LOGUNIT_ASSERT_EQUAL(false, 
asyncAppender->getBlocking());
@@ -602,7 +603,7 @@ class AsyncAppenderTestCase : public 
AppenderSkeletonTestCase
                        asyncAppender->close();
 
                        // Check all message were received
-                       auto vectorAppender = 
log4cxx::cast<VectorAppender>(asyncAppender->getAppender(LOG4CXX_STR("VECTOR")));
+                       auto vectorAppender = 
LOG4CXX_NS::cast<VectorAppender>(asyncAppender->getAppender(LOG4CXX_STR("VECTOR")));
                        LOGUNIT_ASSERT(vectorAppender);
                        auto& v = vectorAppender->getVector();
                        LOGUNIT_ASSERT_EQUAL(LEN, v.size());
@@ -619,7 +620,7 @@ class AsyncAppenderTestCase : public 
AppenderSkeletonTestCase
                        auto  root = Logger::getRootLogger();
                        auto appenders = root->getAllAppenders();
                        LOGUNIT_ASSERT_EQUAL(1, int(appenders.size()));
-                       auto asyncAppender = 
log4cxx::cast<AsyncAppender>(appenders.front());
+                       auto asyncAppender = 
LOG4CXX_NS::cast<AsyncAppender>(appenders.front());
                        LOGUNIT_ASSERT(asyncAppender);
 
                        // Log some messages
@@ -631,12 +632,51 @@ class AsyncAppenderTestCase : public 
AppenderSkeletonTestCase
                        asyncAppender->close();
 
                        // Check all message were received
-                       auto vectorAppender = 
log4cxx::cast<VectorAppender>(asyncAppender->getAppender(LOG4CXX_STR("VECTOR")));
+                       auto vectorAppender = 
LOG4CXX_NS::cast<VectorAppender>(asyncAppender->getAppender(LOG4CXX_STR("VECTOR")));
                        LOGUNIT_ASSERT(vectorAppender);
                        auto& v = vectorAppender->getVector();
                        LOGUNIT_ASSERT_EQUAL(LEN, v.size());
                        LOGUNIT_ASSERT(vectorAppender->isClosed());
                }
+
+               void testRecursiveConfiguration()
+               {
+                       // Configure Log4cxx
+                       auto status = 
xml::DOMConfigurator::configure("input/xml/recursiveConfig.xml");
+                       LOGUNIT_ASSERT_EQUAL(status, 
spi::ConfigurationStatus::Configured);
+
+                       // Check configuration is as expected
+                       auto  root = Logger::getRootLogger();
+                       auto appenders = root->getAllAppenders();
+                       LOGUNIT_ASSERT_EQUAL(1, int(appenders.size()));
+                       auto asyncAppender = 
LOG4CXX_NS::cast<AsyncAppender>(appenders.front());
+                       LOGUNIT_ASSERT(asyncAppender);
+                       VectorAppenderPtr vectorAppender;
+                       for (auto attachedAppender : 
asyncAppender->getAllAppenders())
+                       {
+                               if (auto async2 = 
LOG4CXX_NS::cast<AsyncAppender>(attachedAppender))
+                               {
+                                       for (auto appender : 
async2->getAllAppenders())
+                                               if (vectorAppender = 
LOG4CXX_NS::cast<VectorAppender>(appender))
+                                                       break;
+                               }
+                       }
+                       LOGUNIT_ASSERT(vectorAppender);
+
+                       // Log some messages
+                       size_t LEN = 20;
+                       for (size_t i = 0; i < LEN; i++)
+                       {
+                               LOG4CXX_INFO_ASYNC(root, "message" << i);
+                       }
+                       std::this_thread::sleep_for( std::chrono::milliseconds( 
100 ) );
+                       asyncAppender->close();
+
+                       // Check all message were received
+                       auto& v = vectorAppender->getVector();
+                       LOGUNIT_ASSERT_EQUAL(LEN, v.size());
+                       LOGUNIT_ASSERT(vectorAppender->isClosed());
+               }
 #endif
 
                void testAsyncLoggerProperties()
@@ -649,7 +689,7 @@ class AsyncAppenderTestCase : public 
AppenderSkeletonTestCase
                        auto  root = Logger::getRootLogger();
                        auto appenders = root->getAllAppenders();
                        LOGUNIT_ASSERT_EQUAL(1, int(appenders.size()));
-                       auto asyncAppender = 
log4cxx::cast<AsyncAppender>(appenders.front());
+                       auto asyncAppender = 
LOG4CXX_NS::cast<AsyncAppender>(appenders.front());
                        LOGUNIT_ASSERT(asyncAppender);
 
                        // Log some messages
@@ -661,7 +701,7 @@ class AsyncAppenderTestCase : public 
AppenderSkeletonTestCase
                        asyncAppender->close();
 
                        // Check all message were received
-                       auto vectorAppender = 
log4cxx::cast<VectorAppender>(asyncAppender->getAppender(LOG4CXX_STR("VECTOR")));
+                       auto vectorAppender = 
LOG4CXX_NS::cast<VectorAppender>(asyncAppender->getAppender(LOG4CXX_STR("VECTOR")));
                        LOGUNIT_ASSERT(vectorAppender);
                        auto& v = vectorAppender->getVector();
                        LOGUNIT_ASSERT_EQUAL(LEN, v.size());
diff --git a/src/test/resources/input/xml/recursiveConfig.xml 
b/src/test/resources/input/xml/recursiveConfig.xml
new file mode 100644
index 00000000..716b04c4
--- /dev/null
+++ b/src/test/resources/input/xml/recursiveConfig.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<!--
+ 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.
+
+-->
+
+<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
+
+  <appender name="VECTOR" class="org.apache.log4j.VectorAppender"/>
+
+  <appender name="ASYNC1" class="org.apache.log4j.AsyncAppender">
+    <appender-ref ref="ASYNC2"/>
+    <appender-ref ref="VECTOR"/>
+  </appender>
+
+  <appender name="ASYNC2" class="org.apache.log4j.AsyncAppender">
+    <appender-ref ref="ASYNC1"/>
+  </appender>
+
+  <root>
+    <level value="DEBUG"/>
+    <appender-ref ref="ASYNC2" />
+  </root>
+</log4j:configuration>


Reply via email to