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 307 : ConfigurationUpdaterLock (ConfigurationUpdater& rUpdater)
54 307 : : mrUpdater(rUpdater) { mrUpdater.LockUpdates(); }
55 307 : ~ConfigurationUpdaterLock() { mrUpdater.UnlockUpdates(); }
56 : private:
57 : ConfigurationUpdater& mrUpdater;
58 : };
59 :
60 : //===== ConfigurationUpdater ==================================================
61 :
62 127 : 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 254 : mxCurrentConfiguration(Reference<XConfiguration>(new Configuration(NULL, false))),
69 : mxRequestedConfiguration(),
70 : mbUpdatePending(false),
71 : mbUpdateBeingProcessed(false),
72 : mnLockCount(0),
73 : maUpdateTimer(),
74 : mnFailedUpdateCount(0),
75 381 : 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 127 : maUpdateTimer.SetTimeout(snNormalTimeout);
81 127 : maUpdateTimer.SetTimeoutHdl(LINK(this,ConfigurationUpdater,TimeoutHandler));
82 127 : SetControllerManager(rxControllerManager);
83 127 : }
84 :
85 254 : ConfigurationUpdater::~ConfigurationUpdater()
86 : {
87 127 : maUpdateTimer.Stop();
88 127 : }
89 :
90 127 : void ConfigurationUpdater::SetControllerManager(
91 : const Reference<XControllerManager>& rxControllerManager)
92 : {
93 127 : mxControllerManager = rxControllerManager;
94 127 : }
95 :
96 432 : void ConfigurationUpdater::RequestUpdate (
97 : const Reference<XConfiguration>& rxRequestedConfiguration)
98 : {
99 432 : mxRequestedConfiguration = rxRequestedConfiguration;
100 :
101 : // Find out whether we really can update the configuration.
102 432 : 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 432 : do
109 : {
110 432 : UpdateConfiguration();
111 : }
112 432 : 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 432 : }
120 :
121 432 : bool ConfigurationUpdater::IsUpdatePossible()
122 : {
123 432 : return ! mbUpdateBeingProcessed
124 432 : && mxControllerManager.is()
125 432 : && mnLockCount==0
126 432 : && mxRequestedConfiguration.is()
127 864 : && mxCurrentConfiguration.is();
128 : }
129 :
130 432 : void ConfigurationUpdater::UpdateConfiguration()
131 : {
132 : SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": UpdateConfiguration update");
133 432 : SetUpdateBeingProcessed(true);
134 : comphelper::ScopeGuard aScopeGuard (
135 432 : ::boost::bind(&ConfigurationUpdater::SetUpdateBeingProcessed, this, false));
136 :
137 : try
138 : {
139 432 : mbUpdatePending = false;
140 :
141 432 : CleanRequestedConfiguration();
142 432 : ConfigurationClassifier aClassifier(mxRequestedConfiguration, mxCurrentConfiguration);
143 432 : 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 355 : ConfigurationChangeEvent aEvent;
154 355 : aEvent.Type = FrameworkHelper::msConfigurationUpdateStartEvent;
155 355 : aEvent.Configuration = mxRequestedConfiguration;
156 355 : 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 355 : if (mnLockCount == 0)
163 355 : UpdateCore(aClassifier);
164 : }
165 0 : catch(const RuntimeException&)
166 : {
167 : }
168 :
169 : // Notify the end of the update.
170 355 : aEvent.Type = FrameworkHelper::msConfigurationUpdateEndEvent;
171 355 : mpBroadcaster->NotifyListeners(aEvent);
172 :
173 355 : 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 432 : }
185 : }
186 0 : catch(const RuntimeException &)
187 : {
188 : DBG_UNHANDLED_EXCEPTION();
189 : }
190 :
191 : SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": ConfigurationUpdater::UpdateConfiguration)");
192 432 : SAL_INFO("sd.fwk", OSL_THIS_FUNC << ": UpdateConfiguration end");
193 432 : }
194 :
195 432 : void ConfigurationUpdater::CleanRequestedConfiguration()
196 : {
197 432 : if (mxControllerManager.is())
198 : {
199 : // Request the deactivation of pure anchors that have no child.
200 432 : vector<Reference<XResourceId> > aResourcesToDeactivate;
201 432 : CheckPureAnchors(mxRequestedConfiguration, aResourcesToDeactivate);
202 432 : if (!aResourcesToDeactivate.empty())
203 : {
204 : Reference<XConfigurationController> xCC (
205 2 : mxControllerManager->getConfigurationController());
206 2 : vector<Reference<XResourceId> >::iterator iId;
207 4 : for (iId=aResourcesToDeactivate.begin(); iId!=aResourcesToDeactivate.end(); ++iId)
208 2 : if (iId->is())
209 4 : xCC->requestResourceDeactivation(*iId);
210 432 : }
211 : }
212 432 : }
213 :
214 355 : void ConfigurationUpdater::CheckUpdateSuccess()
215 : {
216 : // When the two configurations differ then start the timer to call
217 : // another update later.
218 355 : if ( ! AreConfigurationsEquivalent(mxCurrentConfiguration, mxRequestedConfiguration))
219 : {
220 177 : if (mnFailedUpdateCount <= snShortTimeoutCountThreshold)
221 141 : maUpdateTimer.SetTimeout(snShortTimeout);
222 36 : else if (mnFailedUpdateCount < snNormalTimeoutCountThreshold)
223 27 : maUpdateTimer.SetTimeout(snNormalTimeout);
224 : else
225 9 : maUpdateTimer.SetTimeout(snLongTimeout);
226 177 : ++mnFailedUpdateCount;
227 177 : maUpdateTimer.Start();
228 : }
229 : else
230 : {
231 : // Update was successful. Reset the failed update count.
232 178 : mnFailedUpdateCount = 0;
233 : }
234 355 : }
235 :
236 355 : 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 355 : mpResourceManager->DeactivateResources(rClassifier.GetC2minusC1(), mxCurrentConfiguration);
254 355 : 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 355 : vector<Reference<XResourceId> > aResourcesToDeactivate;
266 355 : CheckPureAnchors(mxCurrentConfiguration, aResourcesToDeactivate);
267 355 : if (!aResourcesToDeactivate.empty())
268 128 : mpResourceManager->DeactivateResources(aResourcesToDeactivate, mxCurrentConfiguration);
269 : }
270 0 : catch(const RuntimeException&)
271 : {
272 : DBG_UNHANDLED_EXCEPTION();
273 : }
274 355 : }
275 :
276 787 : void ConfigurationUpdater::CheckPureAnchors (
277 : const Reference<XConfiguration>& rxConfiguration,
278 : vector<Reference<XResourceId> >& rResourcesToDeactivate)
279 : {
280 787 : if ( ! rxConfiguration.is())
281 787 : return;
282 :
283 : // Get a list of all resources in the configuration.
284 : Sequence<Reference<XResourceId> > aResources(
285 787 : rxConfiguration->getResources(
286 787 : NULL, OUString(), AnchorBindingMode_INDIRECT));
287 787 : sal_Int32 nCount (aResources.getLength());
288 :
289 : // Prepare the list of pure anchors that have to be deactivated.
290 787 : 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 787 : sal_Int32 nIndex (nCount-1);
296 3916 : while (nIndex >= 0)
297 : {
298 2342 : const Reference<XResourceId> xResourceId (aResources[nIndex]);
299 : const Reference<XResource> xResource (
300 4684 : mpResourceManager->GetResource(xResourceId).mxResource);
301 2342 : bool bDeactiveCurrentResource (false);
302 :
303 : // Skip all resources that are no pure anchors.
304 2342 : 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 329 : 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 329 : const Reference<XResourceId> xPrevResourceId (aResources[nIndex+1]);
317 658 : if ( ! xPrevResourceId.is()
318 329 : || ! xPrevResourceId->isBoundTo(xResourceId, AnchorBindingMode_DIRECT))
319 : {
320 : // The previous resource (id) does not exist or is not bound to
321 : // the current anchor.
322 130 : bDeactiveCurrentResource = true;
323 329 : }
324 : }
325 : }
326 :
327 2342 : 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 304 : for (sal_Int32 nI=nIndex; nI<nCount-2; ++nI)
335 174 : aResources[nI] = aResources[nI+1];
336 130 : nCount -= 1;
337 :
338 130 : rResourcesToDeactivate.push_back(xResourceId);
339 : }
340 2342 : nIndex -= 1;
341 3129 : }
342 : }
343 :
344 307 : void ConfigurationUpdater::LockUpdates()
345 : {
346 307 : ++mnLockCount;
347 307 : }
348 :
349 307 : void ConfigurationUpdater::UnlockUpdates()
350 : {
351 307 : --mnLockCount;
352 307 : if (mnLockCount == 0 && mbUpdatePending)
353 : {
354 0 : RequestUpdate(mxRequestedConfiguration);
355 : }
356 307 : }
357 :
358 307 : ::boost::shared_ptr<ConfigurationUpdaterLock> ConfigurationUpdater::GetLock()
359 : {
360 307 : return ::boost::shared_ptr<ConfigurationUpdaterLock>(new ConfigurationUpdaterLock(*this));
361 : }
362 :
363 864 : void ConfigurationUpdater::SetUpdateBeingProcessed (bool bValue)
364 : {
365 864 : mbUpdateBeingProcessed = bValue;
366 864 : }
367 :
368 148 : IMPL_LINK_NOARG_TYPED(ConfigurationUpdater, TimeoutHandler, Timer *, void)
369 : {
370 : OSL_TRACE("configuration update timer");
371 148 : if ( ! mbUpdateBeingProcessed
372 74 : && mxCurrentConfiguration.is()
373 148 : && mxRequestedConfiguration.is())
374 : {
375 74 : if ( ! AreConfigurationsEquivalent(mxCurrentConfiguration, mxRequestedConfiguration))
376 : {
377 : OSL_TRACE("configurations differ, requesting update");
378 24 : RequestUpdate(mxRequestedConfiguration);
379 : }
380 : }
381 74 : }
382 :
383 66 : } } // end of namespace sd::framework
384 :
385 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|