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 : #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"
26 :
27 : #include <comphelper/scopeguard.hxx>
28 : #include <tools/diagnose_ex.h>
29 :
30 : #include <boost/bind.hpp>
31 :
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;
36 : using ::std::vector;
37 :
38 : namespace {
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);
44 : }
45 :
46 : namespace sd { namespace framework {
47 :
48 : //===== ConfigurationUpdaterLock ==============================================
49 :
50 : class ConfigurationUpdaterLock
51 : {
52 : public:
53 476 : ConfigurationUpdaterLock (ConfigurationUpdater& rUpdater)
54 476 : : mrUpdater(rUpdater) { mrUpdater.LockUpdates(); }
55 476 : ~ConfigurationUpdaterLock(void) { mrUpdater.UnlockUpdates(); }
56 : private:
57 : ConfigurationUpdater& mrUpdater;
58 : };
59 :
60 : //===== ConfigurationUpdater ==================================================
61 :
62 212 : 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 424 : mxCurrentConfiguration(Reference<XConfiguration>(new Configuration(NULL, false))),
69 : mxRequestedConfiguration(),
70 : mbUpdatePending(false),
71 : mbUpdateBeingProcessed(false),
72 : mnLockCount(0),
73 : maUpdateTimer(),
74 : mnFailedUpdateCount(0),
75 636 : mpResourceManager(rpResourceManager)
76 : {
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 212 : maUpdateTimer.SetTimeout(snNormalTimeout);
81 212 : maUpdateTimer.SetTimeoutHdl(LINK(this,ConfigurationUpdater,TimeoutHandler));
82 212 : SetControllerManager(rxControllerManager);
83 212 : }
84 :
85 424 : ConfigurationUpdater::~ConfigurationUpdater (void)
86 : {
87 212 : maUpdateTimer.Stop();
88 212 : }
89 :
90 212 : void ConfigurationUpdater::SetControllerManager(
91 : const Reference<XControllerManager>& rxControllerManager)
92 : {
93 212 : mxControllerManager = rxControllerManager;
94 212 : }
95 :
96 708 : void ConfigurationUpdater::RequestUpdate (
97 : const Reference<XConfiguration>& rxRequestedConfiguration)
98 : {
99 708 : mxRequestedConfiguration = rxRequestedConfiguration;
100 :
101 : // Find out whether we really can update the configuration.
102 708 : if (IsUpdatePossible())
103 : {
104 : SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": UpdateConfiguration start");
105 :
106 : // Call UpdateConfiguration while that is possible and while someone
107 : // set mbUpdatePending to true in the middle of it.
108 708 : do
109 : {
110 708 : UpdateConfiguration();
111 : }
112 708 : while (mbUpdatePending && IsUpdatePossible());
113 : }
114 : else
115 : {
116 0 : mbUpdatePending = true;
117 : SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": scheduling update for later");
118 : }
119 708 : }
120 :
121 708 : bool ConfigurationUpdater::IsUpdatePossible (void)
122 : {
123 708 : return ! mbUpdateBeingProcessed
124 708 : && mxControllerManager.is()
125 708 : && mnLockCount==0
126 708 : && mxRequestedConfiguration.is()
127 1416 : && mxCurrentConfiguration.is();
128 : }
129 :
130 708 : void ConfigurationUpdater::UpdateConfiguration (void)
131 : {
132 : SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": UpdateConfiguration update");
133 708 : SetUpdateBeingProcessed(true);
134 : comphelper::ScopeGuard aScopeGuard (
135 708 : ::boost::bind(&ConfigurationUpdater::SetUpdateBeingProcessed, this, false));
136 :
137 : try
138 : {
139 708 : mbUpdatePending = false;
140 :
141 708 : CleanRequestedConfiguration();
142 708 : ConfigurationClassifier aClassifier(mxRequestedConfiguration, mxCurrentConfiguration);
143 708 : if (aClassifier.Partition())
144 : {
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");
151 : #endif
152 : // Notify the beginning of the update.
153 584 : ConfigurationChangeEvent aEvent;
154 584 : aEvent.Type = FrameworkHelper::msConfigurationUpdateStartEvent;
155 584 : aEvent.Configuration = mxRequestedConfiguration;
156 584 : mpBroadcaster->NotifyListeners(aEvent);
157 :
158 : // Do the actual update. All exceptions are caught and ignored,
159 : // so that the end of the update is notified always.
160 : try
161 : {
162 584 : if (mnLockCount == 0)
163 584 : UpdateCore(aClassifier);
164 : }
165 0 : catch(const RuntimeException&)
166 : {
167 : }
168 :
169 : // Notify the end of the update.
170 584 : aEvent.Type = FrameworkHelper::msConfigurationUpdateEndEvent;
171 584 : mpBroadcaster->NotifyListeners(aEvent);
172 :
173 584 : CheckUpdateSuccess();
174 : }
175 : else
176 : {
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");
183 : #endif
184 708 : }
185 : }
186 0 : catch(const RuntimeException &)
187 : {
188 : DBG_UNHANDLED_EXCEPTION();
189 : }
190 :
191 : SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": ConfigurationUpdater::UpdateConfiguration)");
192 708 : SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": UpdateConfiguration end");
193 708 : }
194 :
195 708 : void ConfigurationUpdater::CleanRequestedConfiguration (void)
196 : {
197 708 : if (mxControllerManager.is())
198 : {
199 : // Request the deactivation of pure anchors that have no child.
200 708 : vector<Reference<XResourceId> > aResourcesToDeactivate;
201 708 : CheckPureAnchors(mxRequestedConfiguration, aResourcesToDeactivate);
202 708 : if (!aResourcesToDeactivate.empty())
203 : {
204 : Reference<XConfigurationController> xCC (
205 0 : mxControllerManager->getConfigurationController());
206 0 : vector<Reference<XResourceId> >::iterator iId;
207 0 : for (iId=aResourcesToDeactivate.begin(); iId!=aResourcesToDeactivate.end(); ++iId)
208 0 : if (iId->is())
209 0 : xCC->requestResourceDeactivation(*iId);
210 708 : }
211 : }
212 708 : }
213 :
214 584 : void ConfigurationUpdater::CheckUpdateSuccess (void)
215 : {
216 : // When the two configurations differ then start the timer to call
217 : // another update later.
218 584 : if ( ! AreConfigurationsEquivalent(mxCurrentConfiguration, mxRequestedConfiguration))
219 : {
220 270 : if (mnFailedUpdateCount <= snShortTimeoutCountThreshold)
221 236 : maUpdateTimer.SetTimeout(snShortTimeout);
222 34 : else if (mnFailedUpdateCount < snNormalTimeoutCountThreshold)
223 34 : maUpdateTimer.SetTimeout(snNormalTimeout);
224 : else
225 0 : maUpdateTimer.SetTimeout(snLongTimeout);
226 270 : ++mnFailedUpdateCount;
227 270 : maUpdateTimer.Start();
228 : }
229 : else
230 : {
231 : // Update was successful. Reset the failed update count.
232 314 : mnFailedUpdateCount = 0;
233 : }
234 584 : }
235 :
236 584 : void ConfigurationUpdater::UpdateCore (const ConfigurationClassifier& rClassifier)
237 : {
238 : try
239 : {
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());
247 : #endif
248 :
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 584 : mpResourceManager->DeactivateResources(rClassifier.GetC2minusC1(), mxCurrentConfiguration);
254 584 : mpResourceManager->ActivateResources(rClassifier.GetC1minusC2(), mxCurrentConfiguration);
255 :
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");
262 : #endif
263 :
264 : // Deactivate pure anchors that have no child.
265 584 : vector<Reference<XResourceId> > aResourcesToDeactivate;
266 584 : CheckPureAnchors(mxCurrentConfiguration, aResourcesToDeactivate);
267 584 : if (!aResourcesToDeactivate.empty())
268 212 : mpResourceManager->DeactivateResources(aResourcesToDeactivate, mxCurrentConfiguration);
269 : }
270 0 : catch(const RuntimeException&)
271 : {
272 : DBG_UNHANDLED_EXCEPTION();
273 : }
274 584 : }
275 :
276 1292 : void ConfigurationUpdater::CheckPureAnchors (
277 : const Reference<XConfiguration>& rxConfiguration,
278 : vector<Reference<XResourceId> >& rResourcesToDeactivate)
279 : {
280 1292 : if ( ! rxConfiguration.is())
281 1292 : return;
282 :
283 : // Get a list of all resources in the configuration.
284 : Sequence<Reference<XResourceId> > aResources(
285 1292 : rxConfiguration->getResources(
286 1292 : NULL, OUString(), AnchorBindingMode_INDIRECT));
287 1292 : sal_Int32 nCount (aResources.getLength());
288 :
289 : // Prepare the list of pure anchors that have to be deactivated.
290 1292 : rResourcesToDeactivate.clear();
291 :
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 1292 : sal_Int32 nIndex (nCount-1);
296 6284 : while (nIndex >= 0)
297 : {
298 3700 : const Reference<XResourceId> xResourceId (aResources[nIndex]);
299 : const Reference<XResource> xResource (
300 7400 : mpResourceManager->GetResource(xResourceId).mxResource);
301 3700 : bool bDeactiveCurrentResource (false);
302 :
303 : // Skip all resources that are no pure anchors.
304 3700 : if (xResource.is() && xResource->isAnchorOnly())
305 : {
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 530 : if (nIndex == nCount-1)
309 : {
310 : // No following anchors, deactivate this one, then remove it
311 : // from the list.
312 0 : bDeactiveCurrentResource = true;
313 : }
314 : else
315 : {
316 530 : const Reference<XResourceId> xPrevResourceId (aResources[nIndex+1]);
317 1060 : if ( ! xPrevResourceId.is()
318 530 : || ! xPrevResourceId->isBoundTo(xResourceId, AnchorBindingMode_DIRECT))
319 : {
320 : // The previous resource (id) does not exist or is not bound to
321 : // the current anchor.
322 212 : bDeactiveCurrentResource = true;
323 530 : }
324 : }
325 : }
326 :
327 3700 : if (bDeactiveCurrentResource)
328 : {
329 : SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": deactiving pure anchor " <<
330 : OUStringToOString(
331 : FrameworkHelper::ResourceIdToString(xResourceId),
332 : RTL_TEXTENCODING_UTF8).getStr() << "because it has no children");
333 : // Erase element from current configuration.
334 476 : for (sal_Int32 nI=nIndex; nI<nCount-2; ++nI)
335 264 : aResources[nI] = aResources[nI+1];
336 212 : nCount -= 1;
337 :
338 212 : rResourcesToDeactivate.push_back(xResourceId);
339 : }
340 3700 : nIndex -= 1;
341 4992 : }
342 : }
343 :
344 476 : void ConfigurationUpdater::LockUpdates (void)
345 : {
346 476 : ++mnLockCount;
347 476 : }
348 :
349 476 : void ConfigurationUpdater::UnlockUpdates (void)
350 : {
351 476 : --mnLockCount;
352 476 : if (mnLockCount == 0 && mbUpdatePending)
353 : {
354 0 : RequestUpdate(mxRequestedConfiguration);
355 : }
356 476 : }
357 :
358 476 : ::boost::shared_ptr<ConfigurationUpdaterLock> ConfigurationUpdater::GetLock (void)
359 : {
360 476 : return ::boost::shared_ptr<ConfigurationUpdaterLock>(new ConfigurationUpdaterLock(*this));
361 : }
362 :
363 1416 : void ConfigurationUpdater::SetUpdateBeingProcessed (bool bValue)
364 : {
365 1416 : mbUpdateBeingProcessed = bValue;
366 1416 : }
367 :
368 224 : IMPL_LINK_NOARG(ConfigurationUpdater, TimeoutHandler)
369 : {
370 : OSL_TRACE("configuration update timer");
371 224 : if ( ! mbUpdateBeingProcessed
372 112 : && mxCurrentConfiguration.is()
373 224 : && mxRequestedConfiguration.is())
374 : {
375 112 : if ( ! AreConfigurationsEquivalent(mxCurrentConfiguration, mxRequestedConfiguration))
376 : {
377 : OSL_TRACE("configurations differ, requesting update");
378 20 : RequestUpdate(mxRequestedConfiguration);
379 : }
380 : }
381 112 : return 0;
382 : }
383 :
384 114 : } } // end of namespace sd::framework
385 :
386 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|