8e9b3abce9
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>
237 lines
8 KiB
C++
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: */
|