office-gobmx/configmgr/source/broadcaster.cxx
Mike Kaganski 8e9b3abce9 Make sure that root listeners are notified first
The problem appeared when in a configuration listener's changesOccurred,
a configuration value was read using officecfg machinery, which could
return the old value of the configuration, or an updated one, at random.

This was caused by use of a cached value in
comphelper::detail::ConfigurationWrapper::getPropertyValue, which is
cleaned in ConfigurationChangesListener::changesOccurred; but the order
in which the listeners' changesOccurred methods were called depended on
the implementation detail of configmgr::Components::roots_, which was
previously a std::set of pointers, and now is a sorted vector. This
made the pointers sorted in order of the pointers' addresses (basically
random), and when a broadcaster's common list of change listeners was
prepared in Components::initGlobalBroadcaster, ConfigurationWrapper's
listener could arrive last. This meant, that the cache could be cleaned
too late, after its obsolete content was already used in a previous
listener.

The problem could be partially mitigated by clearing the cache in the
comphelper::detail::ConfigurationWrapper::setPropertyValue, but that
would only handle cases of config modifications using comphelper.

Instead, take into account that ConfigurationWrapper listens on the
root of configuration tree; and introduce a separate container in
configmgr::Broadcaster for root listeners. They would be triggered
first, before all other listeners.

Still, this would not guarantee the proper order, if another listener
is registered on root. To handle all cases, a special listener category
could be used, which could be filled using a dedicated internal API, so
comphelper could use it to register its privileged listener close to
the heart of the broadcaster :) This might be implemented later.

Change-Id: I956b7989b3927dca2683f63cf92b0dda04db9bdf
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/154561
Tested-by: Jenkins
Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
2023-07-18 06:54:47 +02:00

237 lines
8 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* 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 .
*/
#include <sal/config.h>
#include <cassert>
#include <com/sun/star/beans/XPropertiesChangeListener.hpp>
#include <com/sun/star/beans/XPropertyChangeListener.hpp>
#include <com/sun/star/container/XContainerListener.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
#include <com/sun/star/lang/XEventListener.hpp>
#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/uno/Exception.hpp>
#include <com/sun/star/uno/Reference.hxx>
#include <com/sun/star/uno/XInterface.hpp>
#include <com/sun/star/util/XChangesListener.hpp>
#include <cppuhelper/exc_hlp.hxx>
#include <rtl/ustrbuf.hxx>
#include <rtl/ustring.hxx>
#include <utility>
#include "broadcaster.hxx"
namespace configmgr {
namespace {
void appendMessage(
OUStringBuffer & buffer, css::uno::Exception const & exception)
{
buffer.append("; ");
buffer.append(exception.Message);
}
}
void Broadcaster::addDisposeNotification(
css::uno::Reference< css::lang::XEventListener > const & listener,
css::lang::EventObject const & event)
{
disposeNotifications_.emplace_back(listener, event);
}
void Broadcaster::addContainerElementReplacedNotification(
css::uno::Reference< css::container::XContainerListener > const & listener,
css::container::ContainerEvent const & event)
{
containerElementReplacedNotifications_.emplace_back(listener, event);
}
void Broadcaster::addContainerElementInsertedNotification(
css::uno::Reference< css::container::XContainerListener > const & listener,
css::container::ContainerEvent const & event)
{
containerElementInsertedNotifications_.emplace_back(listener, event);
}
void Broadcaster::addContainerElementRemovedNotification(
css::uno::Reference< css::container::XContainerListener > const & listener,
css::container::ContainerEvent const & event)
{
containerElementRemovedNotifications_.emplace_back(listener, event);
}
void Broadcaster::addPropertyChangeNotification(
css::uno::Reference< css::beans::XPropertyChangeListener > const & listener,
css::beans::PropertyChangeEvent const & event)
{
propertyChangeNotifications_.emplace_back(listener, event);
}
void Broadcaster::addPropertiesChangeNotification(
css::uno::Reference< css::beans::XPropertiesChangeListener > const &
listener,
css::uno::Sequence< css::beans::PropertyChangeEvent > const & event)
{
propertiesChangeNotifications_.emplace_back(listener, event);
}
void Broadcaster::addChangesNotification(
css::uno::Reference< css::util::XChangesListener > const & listener,
css::util::ChangesEvent const & event, bool bRootListener)
{
if (bRootListener)
rootChangesNotifications_.emplace_back(listener, event);
else
changesNotifications_.emplace_back(listener, event);
}
void Broadcaster::send() {
css::uno::Any exception;
OUStringBuffer messages;
for (auto& rNotification : disposeNotifications_) {
try {
rNotification.listener->disposing(rNotification.event);
} catch (css::lang::DisposedException &) {
} catch (css::uno::Exception & e) {
exception = cppu::getCaughtException();
appendMessage(messages, e);
}
}
for (auto& rNotification : containerElementInsertedNotifications_)
{
try {
rNotification.listener->elementInserted(rNotification.event);
} catch (css::lang::DisposedException &) {
} catch (css::uno::Exception & e) {
exception = cppu::getCaughtException();
appendMessage(messages, e);
}
}
for (auto& rNotification : containerElementRemovedNotifications_)
{
try {
rNotification.listener->elementRemoved(rNotification.event);
} catch (css::lang::DisposedException &) {
} catch (css::uno::Exception & e) {
exception = cppu::getCaughtException();
appendMessage(messages, e);
}
}
for (auto& rNotification : containerElementReplacedNotifications_)
{
try {
rNotification.listener->elementReplaced(rNotification.event);
} catch (css::lang::DisposedException &) {
} catch (css::uno::Exception & e) {
exception = cppu::getCaughtException();
appendMessage(messages, e);
}
}
for (auto& rNotification : propertyChangeNotifications_)
{
try {
rNotification.listener->propertyChange(rNotification.event);
} catch (css::lang::DisposedException &) {
} catch (css::uno::Exception & e) {
exception = cppu::getCaughtException();
appendMessage(messages, e);
}
}
for (auto& rNotification : propertiesChangeNotifications_)
{
try {
rNotification.listener->propertiesChange(rNotification.event);
} catch (css::lang::DisposedException &) {
} catch (css::uno::Exception & e) {
exception = cppu::getCaughtException();
appendMessage(messages, e);
}
}
// First root listeners, then the rest
for (const auto& container : { rootChangesNotifications_ , changesNotifications_})
{
for (auto& rNotification : container) {
try {
rNotification.listener->changesOccurred(rNotification.event);
} catch (css::lang::DisposedException &) {
} catch (css::uno::Exception & e) {
exception = cppu::getCaughtException();
appendMessage(messages, e);
}
}
}
if (exception.hasValue()) {
throw css::lang::WrappedTargetRuntimeException(
("configmgr exceptions during listener notification" +
messages),
css::uno::Reference< css::uno::XInterface >(),
exception);
}
}
Broadcaster::DisposeNotification::DisposeNotification(
css::uno::Reference< css::lang::XEventListener > const & theListener,
css::lang::EventObject theEvent):
listener(theListener), event(std::move(theEvent))
{
assert(theListener.is());
}
Broadcaster::ContainerNotification::ContainerNotification(
css::uno::Reference< css::container::XContainerListener > const &
theListener,
css::container::ContainerEvent theEvent):
listener(theListener), event(std::move(theEvent))
{
assert(theListener.is());
}
Broadcaster::PropertyChangeNotification::PropertyChangeNotification(
css::uno::Reference< css::beans::XPropertyChangeListener > const &
theListener,
css::beans::PropertyChangeEvent theEvent):
listener(theListener), event(std::move(theEvent))
{
assert(theListener.is());
}
Broadcaster::PropertiesChangeNotification::PropertiesChangeNotification(
css::uno::Reference< css::beans::XPropertiesChangeListener > const &
theListener,
css::uno::Sequence< css::beans::PropertyChangeEvent > const & theEvent):
listener(theListener), event(theEvent)
{
assert(theListener.is());
}
Broadcaster::ChangesNotification::ChangesNotification(
css::uno::Reference< css::util::XChangesListener > const & theListener,
css::util::ChangesEvent theEvent):
listener(theListener), event(std::move(theEvent))
{
assert(theListener.is());
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */