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