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