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:
64     ConfigurationUpdaterLock (ConfigurationUpdater& rUpdater)
65         : mrUpdater(rUpdater) { mrUpdater.LockUpdates(); }
66     ~ConfigurationUpdaterLock(void) { mrUpdater.UnlockUpdates(); }
67 private:
68     ConfigurationUpdater& mrUpdater;
69 };
70 
71 
72 
73 
74 //===== ConfigurationUpdater ==================================================
75 
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 
102 ConfigurationUpdater::~ConfigurationUpdater (void)
103 {
104     maUpdateTimer.Stop();
105 }
106 
107 
108 
109 
110 void ConfigurationUpdater::SetControllerManager(
111     const Reference<XControllerManager>& rxControllerManager)
112 {
113     mxControllerManager = rxControllerManager;
114 }
115 
116 
117 
118 
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 
154 Reference<XConfiguration> ConfigurationUpdater::GetCurrentConfiguration (void) const
155 {
156     return mxCurrentConfiguration;
157 }
158 
159 
160 
161 
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 
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 beginning 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 
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 
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 successful.  Reset the failed update count.
288         mnFailedUpdateCount = 0;
289     }
290 }
291 
292 
293 
294 
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 
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 
411 void ConfigurationUpdater::LockUpdates (void)
412 {
413     ++mnLockCount;
414 }
415 
416 
417 
418 
419 void ConfigurationUpdater::UnlockUpdates (void)
420 {
421     --mnLockCount;
422     if (mnLockCount == 0 && mbUpdatePending)
423     {
424         RequestUpdate(mxRequestedConfiguration);
425     }
426 }
427 
428 
429 
430 
431 ::boost::shared_ptr<ConfigurationUpdaterLock> ConfigurationUpdater::GetLock (void)
432 {
433     return ::boost::shared_ptr<ConfigurationUpdaterLock>(new ConfigurationUpdaterLock(*this));
434 }
435 
436 
437 
438 
439 void ConfigurationUpdater::SetUpdateBeingProcessed (bool bValue)
440 {
441     mbUpdateBeingProcessed = bValue;
442 }
443 
444 
445 
446 
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