Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 :
21 : #include "ConfigurationUpdater.hxx"
22 : #include "ConfigurationTracer.hxx"
23 : #include "ConfigurationClassifier.hxx"
24 : #include "ConfigurationControllerBroadcaster.hxx"
25 : #include "framework/Configuration.hxx"
26 : #include "framework/FrameworkHelper.hxx"
27 :
28 : #include <comphelper/scopeguard.hxx>
29 : #include <tools/diagnose_ex.h>
30 :
31 : #include <boost/bind.hpp>
32 :
33 : using namespace ::com::sun::star;
34 : using namespace ::com::sun::star::uno;
35 : using namespace ::com::sun::star::drawing::framework;
36 : using ::sd::framework::FrameworkHelper;
37 : using ::std::vector;
38 :
39 : namespace {
40 : static const sal_Int32 snShortTimeout (100);
41 : static const sal_Int32 snNormalTimeout (1000);
42 : static const sal_Int32 snLongTimeout (10000);
43 : static const sal_Int32 snShortTimeoutCountThreshold (1);
44 : static const sal_Int32 snNormalTimeoutCountThreshold (5);
45 : }
46 :
47 : namespace sd { namespace framework {
48 :
49 :
50 : //===== ConfigurationUpdaterLock ==============================================
51 :
52 : class ConfigurationUpdaterLock
53 : {
54 : public:
55 0 : ConfigurationUpdaterLock (ConfigurationUpdater& rUpdater)
56 0 : : mrUpdater(rUpdater) { mrUpdater.LockUpdates(); }
57 0 : ~ConfigurationUpdaterLock(void) { mrUpdater.UnlockUpdates(); }
58 : private:
59 : ConfigurationUpdater& mrUpdater;
60 : };
61 :
62 :
63 :
64 :
65 : //===== ConfigurationUpdater ==================================================
66 :
67 0 : ConfigurationUpdater::ConfigurationUpdater (
68 : const ::boost::shared_ptr<ConfigurationControllerBroadcaster>& rpBroadcaster,
69 : const ::boost::shared_ptr<ConfigurationControllerResourceManager>& rpResourceManager,
70 : const Reference<XControllerManager>& rxControllerManager)
71 : : mxControllerManager(),
72 : mpBroadcaster(rpBroadcaster),
73 0 : mxCurrentConfiguration(Reference<XConfiguration>(new Configuration(NULL, false))),
74 : mxRequestedConfiguration(),
75 : mbUpdatePending(false),
76 : mbUpdateBeingProcessed(false),
77 : mnLockCount(0),
78 : maUpdateTimer(),
79 : mnFailedUpdateCount(0),
80 0 : mpResourceManager(rpResourceManager)
81 : {
82 : // Prepare the timer that is started when after an update the current
83 : // and the requested configuration differ. With the timer we try
84 : // updates until the two configurations are the same.
85 0 : maUpdateTimer.SetTimeout(snNormalTimeout);
86 0 : maUpdateTimer.SetTimeoutHdl(LINK(this,ConfigurationUpdater,TimeoutHandler));
87 0 : SetControllerManager(rxControllerManager);
88 0 : }
89 :
90 :
91 :
92 :
93 0 : ConfigurationUpdater::~ConfigurationUpdater (void)
94 : {
95 0 : maUpdateTimer.Stop();
96 0 : }
97 :
98 :
99 :
100 :
101 0 : void ConfigurationUpdater::SetControllerManager(
102 : const Reference<XControllerManager>& rxControllerManager)
103 : {
104 0 : mxControllerManager = rxControllerManager;
105 0 : }
106 :
107 :
108 :
109 :
110 0 : void ConfigurationUpdater::RequestUpdate (
111 : const Reference<XConfiguration>& rxRequestedConfiguration)
112 : {
113 0 : mxRequestedConfiguration = rxRequestedConfiguration;
114 :
115 : // Find out whether we really can update the configuration.
116 0 : if (IsUpdatePossible())
117 : {
118 : SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": UpdateConfiguration start");
119 :
120 : // Call UpdateConfiguration while that is possible and while someone
121 : // set mbUpdatePending to true in the middle of it.
122 : do
123 : {
124 0 : UpdateConfiguration();
125 :
126 0 : if (mbUpdatePending && IsUpdatePossible())
127 0 : continue;
128 : }
129 : while (false);
130 : }
131 : else
132 : {
133 0 : mbUpdatePending = true;
134 : SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": scheduling update for later");
135 : }
136 0 : }
137 :
138 :
139 :
140 :
141 0 : Reference<XConfiguration> ConfigurationUpdater::GetCurrentConfiguration (void) const
142 : {
143 0 : return mxCurrentConfiguration;
144 : }
145 :
146 :
147 :
148 :
149 0 : bool ConfigurationUpdater::IsUpdatePossible (void)
150 : {
151 0 : return ! mbUpdateBeingProcessed
152 0 : && mxControllerManager.is()
153 0 : && mnLockCount==0
154 0 : && mxRequestedConfiguration.is()
155 0 : && mxCurrentConfiguration.is();
156 : }
157 :
158 :
159 :
160 :
161 0 : void ConfigurationUpdater::UpdateConfiguration (void)
162 : {
163 : SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": UpdateConfiguration update");
164 0 : SetUpdateBeingProcessed(true);
165 : comphelper::ScopeGuard aScopeGuard (
166 0 : ::boost::bind(&ConfigurationUpdater::SetUpdateBeingProcessed, this, false));
167 :
168 : try
169 : {
170 0 : mbUpdatePending = false;
171 :
172 0 : CleanRequestedConfiguration();
173 0 : ConfigurationClassifier aClassifier(mxRequestedConfiguration, mxCurrentConfiguration);
174 0 : if (aClassifier.Partition())
175 : {
176 : #if OSL_DEBUG_LEVEL >= 2
177 : SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": ConfigurationUpdater::UpdateConfiguration(");
178 : ConfigurationTracer::TraceConfiguration(
179 : mxRequestedConfiguration, "requested configuration");
180 : ConfigurationTracer::TraceConfiguration(
181 : mxCurrentConfiguration, "current configuration");
182 : #endif
183 : // Notify the beginning of the update.
184 0 : ConfigurationChangeEvent aEvent;
185 0 : aEvent.Type = FrameworkHelper::msConfigurationUpdateStartEvent;
186 0 : aEvent.Configuration = mxRequestedConfiguration;
187 0 : mpBroadcaster->NotifyListeners(aEvent);
188 :
189 : // Do the actual update. All exceptions are caught and ignored,
190 : // so that the end of the update is notified always.
191 : try
192 : {
193 0 : if (mnLockCount == 0)
194 0 : UpdateCore(aClassifier);
195 : }
196 0 : catch(const RuntimeException&)
197 : {
198 : }
199 :
200 : // Notify the end of the update.
201 0 : aEvent.Type = FrameworkHelper::msConfigurationUpdateEndEvent;
202 0 : mpBroadcaster->NotifyListeners(aEvent);
203 :
204 0 : CheckUpdateSuccess();
205 : }
206 : else
207 : {
208 : SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": nothing to do");
209 : #if OSL_DEBUG_LEVEL >= 2
210 : ConfigurationTracer::TraceConfiguration(
211 : mxRequestedConfiguration, "requested configuration");
212 : ConfigurationTracer::TraceConfiguration(
213 : mxCurrentConfiguration, "current configuration");
214 : #endif
215 0 : }
216 : }
217 0 : catch(const RuntimeException &)
218 : {
219 : DBG_UNHANDLED_EXCEPTION();
220 : }
221 :
222 : SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": ConfigurationUpdater::UpdateConfiguration)");
223 0 : SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": UpdateConfiguration end");
224 0 : }
225 :
226 :
227 :
228 :
229 0 : void ConfigurationUpdater::CleanRequestedConfiguration (void)
230 : {
231 0 : if (mxControllerManager.is())
232 : {
233 : // Request the deactivation of pure anchors that have no child.
234 0 : vector<Reference<XResourceId> > aResourcesToDeactivate;
235 0 : CheckPureAnchors(mxRequestedConfiguration, aResourcesToDeactivate);
236 0 : if (!aResourcesToDeactivate.empty())
237 : {
238 : Reference<XConfigurationController> xCC (
239 0 : mxControllerManager->getConfigurationController());
240 0 : vector<Reference<XResourceId> >::iterator iId;
241 0 : for (iId=aResourcesToDeactivate.begin(); iId!=aResourcesToDeactivate.end(); ++iId)
242 0 : if (iId->is())
243 0 : xCC->requestResourceDeactivation(*iId);
244 0 : }
245 : }
246 0 : }
247 :
248 :
249 :
250 :
251 0 : void ConfigurationUpdater::CheckUpdateSuccess (void)
252 : {
253 : // When the two configurations differ then start the timer to call
254 : // another update later.
255 0 : if ( ! AreConfigurationsEquivalent(mxCurrentConfiguration, mxRequestedConfiguration))
256 : {
257 0 : if (mnFailedUpdateCount <= snShortTimeoutCountThreshold)
258 0 : maUpdateTimer.SetTimeout(snShortTimeout);
259 0 : else if (mnFailedUpdateCount < snNormalTimeoutCountThreshold)
260 0 : maUpdateTimer.SetTimeout(snNormalTimeout);
261 : else
262 0 : maUpdateTimer.SetTimeout(snLongTimeout);
263 0 : ++mnFailedUpdateCount;
264 0 : maUpdateTimer.Start();
265 : }
266 : else
267 : {
268 : // Update was successful. Reset the failed update count.
269 0 : mnFailedUpdateCount = 0;
270 : }
271 0 : }
272 :
273 :
274 :
275 :
276 0 : void ConfigurationUpdater::UpdateCore (const ConfigurationClassifier& rClassifier)
277 : {
278 : try
279 : {
280 : #if OSL_DEBUG_LEVEL >= 2
281 : rClassifier.TraceResourceIdVector(
282 : "requested but not current resources:", rClassifier.GetC1minusC2());
283 : rClassifier.TraceResourceIdVector(
284 : "current but not requested resources:", rClassifier.GetC2minusC1());
285 : rClassifier.TraceResourceIdVector(
286 : "requested and current resources:", rClassifier.GetC1andC2());
287 : #endif
288 :
289 : // Updating of the sub controllers is done in two steps. In the
290 : // first the sub controllers typically shut down resources that are
291 : // not requested anymore. In the second the sub controllers
292 : // typically set up resources that have been newly requested.
293 0 : mpResourceManager->DeactivateResources(rClassifier.GetC2minusC1(), mxCurrentConfiguration);
294 0 : mpResourceManager->ActivateResources(rClassifier.GetC1minusC2(), mxCurrentConfiguration);
295 :
296 : #if OSL_DEBUG_LEVEL >= 2
297 : SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": ConfigurationController::UpdateConfiguration)");
298 : ConfigurationTracer::TraceConfiguration(
299 : mxRequestedConfiguration, "requested configuration");
300 : ConfigurationTracer::TraceConfiguration(
301 : mxCurrentConfiguration, "current configuration");
302 : #endif
303 :
304 : // Deactivate pure anchors that have no child.
305 0 : vector<Reference<XResourceId> > aResourcesToDeactivate;
306 0 : CheckPureAnchors(mxCurrentConfiguration, aResourcesToDeactivate);
307 0 : if (!aResourcesToDeactivate.empty())
308 0 : mpResourceManager->DeactivateResources(aResourcesToDeactivate, mxCurrentConfiguration);
309 : }
310 0 : catch(const RuntimeException&)
311 : {
312 : DBG_UNHANDLED_EXCEPTION();
313 : }
314 0 : }
315 :
316 :
317 :
318 :
319 0 : void ConfigurationUpdater::CheckPureAnchors (
320 : const Reference<XConfiguration>& rxConfiguration,
321 : vector<Reference<XResourceId> >& rResourcesToDeactivate)
322 : {
323 0 : if ( ! rxConfiguration.is())
324 0 : return;
325 :
326 : // Get a list of all resources in the configuration.
327 : Sequence<Reference<XResourceId> > aResources(
328 0 : rxConfiguration->getResources(
329 0 : NULL, OUString(), AnchorBindingMode_INDIRECT));
330 0 : sal_Int32 nCount (aResources.getLength());
331 :
332 : // Prepare the list of pure anchors that have to be deactivated.
333 0 : rResourcesToDeactivate.clear();
334 :
335 : // Iterate over the list in reverse order because when there is a chain
336 : // of pure anchors with only the last one having no child then the whole
337 : // list has to be deactivated.
338 0 : sal_Int32 nIndex (nCount-1);
339 0 : while (nIndex >= 0)
340 : {
341 0 : const Reference<XResourceId> xResourceId (aResources[nIndex]);
342 : const Reference<XResource> xResource (
343 0 : mpResourceManager->GetResource(xResourceId).mxResource);
344 0 : bool bDeactiveCurrentResource (false);
345 :
346 : // Skip all resources that are no pure anchors.
347 0 : if (xResource.is() && xResource->isAnchorOnly())
348 : {
349 : // When xResource is not an anchor of the next resource in
350 : // the list then it is the anchor of no resource at all.
351 0 : if (nIndex == nCount-1)
352 : {
353 : // No following anchors, deactivate this one, then remove it
354 : // from the list.
355 0 : bDeactiveCurrentResource = true;
356 : }
357 : else
358 : {
359 0 : const Reference<XResourceId> xPrevResourceId (aResources[nIndex+1]);
360 0 : if ( ! xPrevResourceId.is()
361 0 : || ! xPrevResourceId->isBoundTo(xResourceId, AnchorBindingMode_DIRECT))
362 : {
363 : // The previous resource (id) does not exist or is not bound to
364 : // the current anchor.
365 0 : bDeactiveCurrentResource = true;
366 0 : }
367 : }
368 : }
369 :
370 0 : if (bDeactiveCurrentResource)
371 : {
372 : SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": deactiving pure anchor " <<
373 : OUStringToOString(
374 : FrameworkHelper::ResourceIdToString(xResourceId),
375 : RTL_TEXTENCODING_UTF8).getStr() << "because it has no children");
376 : // Erase element from current configuration.
377 0 : for (sal_Int32 nI=nIndex; nI<nCount-2; ++nI)
378 0 : aResources[nI] = aResources[nI+1];
379 0 : nCount -= 1;
380 :
381 0 : rResourcesToDeactivate.push_back(xResourceId);
382 : }
383 0 : nIndex -= 1;
384 0 : }
385 : }
386 :
387 :
388 :
389 :
390 0 : void ConfigurationUpdater::LockUpdates (void)
391 : {
392 0 : ++mnLockCount;
393 0 : }
394 :
395 :
396 :
397 :
398 0 : void ConfigurationUpdater::UnlockUpdates (void)
399 : {
400 0 : --mnLockCount;
401 0 : if (mnLockCount == 0 && mbUpdatePending)
402 : {
403 0 : RequestUpdate(mxRequestedConfiguration);
404 : }
405 0 : }
406 :
407 :
408 :
409 :
410 0 : ::boost::shared_ptr<ConfigurationUpdaterLock> ConfigurationUpdater::GetLock (void)
411 : {
412 0 : return ::boost::shared_ptr<ConfigurationUpdaterLock>(new ConfigurationUpdaterLock(*this));
413 : }
414 :
415 :
416 :
417 :
418 0 : void ConfigurationUpdater::SetUpdateBeingProcessed (bool bValue)
419 : {
420 0 : mbUpdateBeingProcessed = bValue;
421 0 : }
422 :
423 :
424 :
425 :
426 0 : IMPL_LINK_NOARG(ConfigurationUpdater, TimeoutHandler)
427 : {
428 : OSL_TRACE("configuration update timer");
429 0 : if ( ! mbUpdateBeingProcessed
430 0 : && mxCurrentConfiguration.is()
431 0 : && mxRequestedConfiguration.is())
432 : {
433 0 : if ( ! AreConfigurationsEquivalent(mxCurrentConfiguration, mxRequestedConfiguration))
434 : {
435 : OSL_TRACE("configurations differ, requesting update");
436 0 : RequestUpdate(mxRequestedConfiguration);
437 : }
438 : }
439 0 : return 0;
440 : }
441 :
442 :
443 0 : } } // end of namespace sd::framework
444 :
445 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|