1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "ConfigurationUpdater.hxx"
21 #include "ConfigurationTracer.hxx"
22 #include "ConfigurationClassifier.hxx"
23 #include "ConfigurationControllerBroadcaster.hxx"
24 #include "framework/Configuration.hxx"
25 #include "framework/FrameworkHelper.hxx"
27 #include <comphelper/scopeguard.hxx>
28 #include <tools/diagnose_ex.h>
30 #include <boost/bind.hpp>
32 using namespace ::com::sun::star
;
33 using namespace ::com::sun::star::uno
;
34 using namespace ::com::sun::star::drawing::framework
;
35 using ::sd::framework::FrameworkHelper
;
39 static const sal_Int32
snShortTimeout (100);
40 static const sal_Int32
snNormalTimeout (1000);
41 static const sal_Int32
snLongTimeout (10000);
42 static const sal_Int32
snShortTimeoutCountThreshold (1);
43 static const sal_Int32
snNormalTimeoutCountThreshold (5);
46 namespace sd
{ namespace framework
{
48 //===== ConfigurationUpdaterLock ==============================================
50 class ConfigurationUpdaterLock
53 ConfigurationUpdaterLock (ConfigurationUpdater
& rUpdater
)
54 : mrUpdater(rUpdater
) { mrUpdater
.LockUpdates(); }
55 ~ConfigurationUpdaterLock() { mrUpdater
.UnlockUpdates(); }
57 ConfigurationUpdater
& mrUpdater
;
60 //===== ConfigurationUpdater ==================================================
62 ConfigurationUpdater::ConfigurationUpdater (
63 const ::boost::shared_ptr
<ConfigurationControllerBroadcaster
>& rpBroadcaster
,
64 const ::boost::shared_ptr
<ConfigurationControllerResourceManager
>& rpResourceManager
,
65 const Reference
<XControllerManager
>& rxControllerManager
)
66 : mxControllerManager(),
67 mpBroadcaster(rpBroadcaster
),
68 mxCurrentConfiguration(Reference
<XConfiguration
>(new Configuration(NULL
, false))),
69 mxRequestedConfiguration(),
70 mbUpdatePending(false),
71 mbUpdateBeingProcessed(false),
74 mnFailedUpdateCount(0),
75 mpResourceManager(rpResourceManager
)
77 // Prepare the timer that is started when after an update the current
78 // and the requested configuration differ. With the timer we try
79 // updates until the two configurations are the same.
80 maUpdateTimer
.SetTimeout(snNormalTimeout
);
81 maUpdateTimer
.SetTimeoutHdl(LINK(this,ConfigurationUpdater
,TimeoutHandler
));
82 SetControllerManager(rxControllerManager
);
85 ConfigurationUpdater::~ConfigurationUpdater()
90 void ConfigurationUpdater::SetControllerManager(
91 const Reference
<XControllerManager
>& rxControllerManager
)
93 mxControllerManager
= rxControllerManager
;
96 void ConfigurationUpdater::RequestUpdate (
97 const Reference
<XConfiguration
>& rxRequestedConfiguration
)
99 mxRequestedConfiguration
= rxRequestedConfiguration
;
101 // Find out whether we really can update the configuration.
102 if (IsUpdatePossible())
104 SAL_INFO("sd.fwk", OSL_THIS_FUNC
<< ": UpdateConfiguration start");
106 // Call UpdateConfiguration while that is possible and while someone
107 // set mbUpdatePending to true in the middle of it.
110 UpdateConfiguration();
112 while (mbUpdatePending
&& IsUpdatePossible());
116 mbUpdatePending
= true;
117 SAL_INFO("sd.fwk", OSL_THIS_FUNC
<< ": scheduling update for later");
121 bool ConfigurationUpdater::IsUpdatePossible()
123 return ! mbUpdateBeingProcessed
124 && mxControllerManager
.is()
126 && mxRequestedConfiguration
.is()
127 && mxCurrentConfiguration
.is();
130 void ConfigurationUpdater::UpdateConfiguration()
132 SAL_INFO("sd.fwk", OSL_THIS_FUNC
<< ": UpdateConfiguration update");
133 SetUpdateBeingProcessed(true);
134 comphelper::ScopeGuard
aScopeGuard (
135 ::boost::bind(&ConfigurationUpdater::SetUpdateBeingProcessed
, this, false));
139 mbUpdatePending
= false;
141 CleanRequestedConfiguration();
142 ConfigurationClassifier
aClassifier(mxRequestedConfiguration
, mxCurrentConfiguration
);
143 if (aClassifier
.Partition())
145 #if OSL_DEBUG_LEVEL >= 2
146 SAL_INFO("sd.fwk", OSL_THIS_FUNC
<< ": ConfigurationUpdater::UpdateConfiguration(");
147 ConfigurationTracer::TraceConfiguration(
148 mxRequestedConfiguration
, "requested configuration");
149 ConfigurationTracer::TraceConfiguration(
150 mxCurrentConfiguration
, "current configuration");
152 // Notify the beginning of the update.
153 ConfigurationChangeEvent aEvent
;
154 aEvent
.Type
= FrameworkHelper::msConfigurationUpdateStartEvent
;
155 aEvent
.Configuration
= mxRequestedConfiguration
;
156 mpBroadcaster
->NotifyListeners(aEvent
);
158 // Do the actual update. All exceptions are caught and ignored,
159 // so that the end of the update is notified always.
162 if (mnLockCount
== 0)
163 UpdateCore(aClassifier
);
165 catch(const RuntimeException
&)
169 // Notify the end of the update.
170 aEvent
.Type
= FrameworkHelper::msConfigurationUpdateEndEvent
;
171 mpBroadcaster
->NotifyListeners(aEvent
);
173 CheckUpdateSuccess();
177 SAL_INFO("sd.fwk", OSL_THIS_FUNC
<< ": nothing to do");
178 #if OSL_DEBUG_LEVEL >= 2
179 ConfigurationTracer::TraceConfiguration(
180 mxRequestedConfiguration
, "requested configuration");
181 ConfigurationTracer::TraceConfiguration(
182 mxCurrentConfiguration
, "current configuration");
186 catch(const RuntimeException
&)
188 DBG_UNHANDLED_EXCEPTION();
191 SAL_INFO("sd.fwk", OSL_THIS_FUNC
<< ": ConfigurationUpdater::UpdateConfiguration)");
192 SAL_INFO("sd.fwk", OSL_THIS_FUNC
<< ": UpdateConfiguration end");
195 void ConfigurationUpdater::CleanRequestedConfiguration()
197 if (mxControllerManager
.is())
199 // Request the deactivation of pure anchors that have no child.
200 vector
<Reference
<XResourceId
> > aResourcesToDeactivate
;
201 CheckPureAnchors(mxRequestedConfiguration
, aResourcesToDeactivate
);
202 if (!aResourcesToDeactivate
.empty())
204 Reference
<XConfigurationController
> xCC (
205 mxControllerManager
->getConfigurationController());
206 vector
<Reference
<XResourceId
> >::iterator iId
;
207 for (iId
=aResourcesToDeactivate
.begin(); iId
!=aResourcesToDeactivate
.end(); ++iId
)
209 xCC
->requestResourceDeactivation(*iId
);
214 void ConfigurationUpdater::CheckUpdateSuccess()
216 // When the two configurations differ then start the timer to call
217 // another update later.
218 if ( ! AreConfigurationsEquivalent(mxCurrentConfiguration
, mxRequestedConfiguration
))
220 if (mnFailedUpdateCount
<= snShortTimeoutCountThreshold
)
221 maUpdateTimer
.SetTimeout(snShortTimeout
);
222 else if (mnFailedUpdateCount
< snNormalTimeoutCountThreshold
)
223 maUpdateTimer
.SetTimeout(snNormalTimeout
);
225 maUpdateTimer
.SetTimeout(snLongTimeout
);
226 ++mnFailedUpdateCount
;
227 maUpdateTimer
.Start();
231 // Update was successful. Reset the failed update count.
232 mnFailedUpdateCount
= 0;
236 void ConfigurationUpdater::UpdateCore (const ConfigurationClassifier
& rClassifier
)
240 #if OSL_DEBUG_LEVEL >= 2
241 rClassifier
.TraceResourceIdVector(
242 "requested but not current resources:", rClassifier
.GetC1minusC2());
243 rClassifier
.TraceResourceIdVector(
244 "current but not requested resources:", rClassifier
.GetC2minusC1());
245 rClassifier
.TraceResourceIdVector(
246 "requested and current resources:", rClassifier
.GetC1andC2());
249 // Updating of the sub controllers is done in two steps. In the
250 // first the sub controllers typically shut down resources that are
251 // not requested anymore. In the second the sub controllers
252 // typically set up resources that have been newly requested.
253 mpResourceManager
->DeactivateResources(rClassifier
.GetC2minusC1(), mxCurrentConfiguration
);
254 mpResourceManager
->ActivateResources(rClassifier
.GetC1minusC2(), mxCurrentConfiguration
);
256 #if OSL_DEBUG_LEVEL >= 2
257 SAL_INFO("sd.fwk", OSL_THIS_FUNC
<< ": ConfigurationController::UpdateConfiguration)");
258 ConfigurationTracer::TraceConfiguration(
259 mxRequestedConfiguration
, "requested configuration");
260 ConfigurationTracer::TraceConfiguration(
261 mxCurrentConfiguration
, "current configuration");
264 // Deactivate pure anchors that have no child.
265 vector
<Reference
<XResourceId
> > aResourcesToDeactivate
;
266 CheckPureAnchors(mxCurrentConfiguration
, aResourcesToDeactivate
);
267 if (!aResourcesToDeactivate
.empty())
268 mpResourceManager
->DeactivateResources(aResourcesToDeactivate
, mxCurrentConfiguration
);
270 catch(const RuntimeException
&)
272 DBG_UNHANDLED_EXCEPTION();
276 void ConfigurationUpdater::CheckPureAnchors (
277 const Reference
<XConfiguration
>& rxConfiguration
,
278 vector
<Reference
<XResourceId
> >& rResourcesToDeactivate
)
280 if ( ! rxConfiguration
.is())
283 // Get a list of all resources in the configuration.
284 Sequence
<Reference
<XResourceId
> > aResources(
285 rxConfiguration
->getResources(
286 NULL
, OUString(), AnchorBindingMode_INDIRECT
));
287 sal_Int32
nCount (aResources
.getLength());
289 // Prepare the list of pure anchors that have to be deactivated.
290 rResourcesToDeactivate
.clear();
292 // Iterate over the list in reverse order because when there is a chain
293 // of pure anchors with only the last one having no child then the whole
294 // list has to be deactivated.
295 sal_Int32
nIndex (nCount
-1);
298 const Reference
<XResourceId
> xResourceId (aResources
[nIndex
]);
299 const Reference
<XResource
> xResource (
300 mpResourceManager
->GetResource(xResourceId
).mxResource
);
301 bool bDeactiveCurrentResource (false);
303 // Skip all resources that are no pure anchors.
304 if (xResource
.is() && xResource
->isAnchorOnly())
306 // When xResource is not an anchor of the next resource in
307 // the list then it is the anchor of no resource at all.
308 if (nIndex
== nCount
-1)
310 // No following anchors, deactivate this one, then remove it
312 bDeactiveCurrentResource
= true;
316 const Reference
<XResourceId
> xPrevResourceId (aResources
[nIndex
+1]);
317 if ( ! xPrevResourceId
.is()
318 || ! xPrevResourceId
->isBoundTo(xResourceId
, AnchorBindingMode_DIRECT
))
320 // The previous resource (id) does not exist or is not bound to
321 // the current anchor.
322 bDeactiveCurrentResource
= true;
327 if (bDeactiveCurrentResource
)
329 SAL_INFO("sd.fwk", OSL_THIS_FUNC
<< ": deactiving pure anchor " <<
331 FrameworkHelper::ResourceIdToString(xResourceId
),
332 RTL_TEXTENCODING_UTF8
).getStr() << "because it has no children");
333 // Erase element from current configuration.
334 for (sal_Int32 nI
=nIndex
; nI
<nCount
-2; ++nI
)
335 aResources
[nI
] = aResources
[nI
+1];
338 rResourcesToDeactivate
.push_back(xResourceId
);
344 void ConfigurationUpdater::LockUpdates()
349 void ConfigurationUpdater::UnlockUpdates()
352 if (mnLockCount
== 0 && mbUpdatePending
)
354 RequestUpdate(mxRequestedConfiguration
);
358 ::boost::shared_ptr
<ConfigurationUpdaterLock
> ConfigurationUpdater::GetLock()
360 return ::boost::shared_ptr
<ConfigurationUpdaterLock
>(new ConfigurationUpdaterLock(*this));
363 void ConfigurationUpdater::SetUpdateBeingProcessed (bool bValue
)
365 mbUpdateBeingProcessed
= bValue
;
368 IMPL_LINK_NOARG_TYPED(ConfigurationUpdater
, TimeoutHandler
, Timer
*, void)
370 OSL_TRACE("configuration update timer");
371 if ( ! mbUpdateBeingProcessed
372 && mxCurrentConfiguration
.is()
373 && mxRequestedConfiguration
.is())
375 if ( ! AreConfigurationsEquivalent(mxCurrentConfiguration
, mxRequestedConfiguration
))
377 OSL_TRACE("configurations differ, requesting update");
378 RequestUpdate(mxRequestedConfiguration
);
383 } } // end of namespace sd::framework
385 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */