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