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