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 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_framework.hxx" 30 31 //________________________________ 32 // my own includes 33 #include <jobs/jobexecutor.hxx> 34 #include <jobs/job.hxx> 35 #include <jobs/joburl.hxx> 36 37 #ifndef __FRAMEWORK_CLASS_CONVERTER_HXX_ 38 #include <classes/converter.hxx> 39 #endif 40 #include <threadhelp/transactionguard.hxx> 41 #include <threadhelp/readguard.hxx> 42 #include <threadhelp/writeguard.hxx> 43 #include <general.h> 44 #include <services.h> 45 46 //________________________________ 47 // interface includes 48 #include <com/sun/star/beans/XPropertySet.hpp> 49 #include <com/sun/star/container/XNameAccess.hpp> 50 #include <com/sun/star/container/XContainer.hpp> 51 52 //________________________________ 53 // includes of other projects 54 #include <unotools/configpathes.hxx> 55 #include <rtl/ustrbuf.hxx> 56 #include <vcl/svapp.hxx> 57 58 #include <rtl/logfile.hxx> 59 60 //________________________________ 61 // namespace 62 63 namespace framework{ 64 65 //________________________________ 66 // non exported const 67 68 //________________________________ 69 // non exported definitions 70 71 //________________________________ 72 // declarations 73 74 DEFINE_XINTERFACE_6( JobExecutor , 75 OWeakObject , 76 DIRECT_INTERFACE(css::lang::XTypeProvider ), 77 DIRECT_INTERFACE(css::lang::XServiceInfo ), 78 DIRECT_INTERFACE(css::task::XJobExecutor ), 79 DIRECT_INTERFACE(css::container::XContainerListener ), 80 DIRECT_INTERFACE(css::document::XEventListener ), 81 DERIVED_INTERFACE(css::lang::XEventListener,css::document::XEventListener) 82 ) 83 84 DEFINE_XTYPEPROVIDER_6( JobExecutor , 85 css::lang::XTypeProvider , 86 css::lang::XServiceInfo , 87 css::task::XJobExecutor , 88 css::container::XContainerListener, 89 css::document::XEventListener , 90 css::lang::XEventListener 91 ) 92 93 DEFINE_XSERVICEINFO_ONEINSTANCESERVICE( JobExecutor , 94 ::cppu::OWeakObject , 95 SERVICENAME_JOBEXECUTOR , 96 IMPLEMENTATIONNAME_JOBEXECUTOR 97 ) 98 99 DEFINE_INIT_SERVICE( JobExecutor, 100 { 101 m_xModuleManager = css::uno::Reference< css::frame::XModuleManager >( 102 m_xSMGR->createInstance( 103 SERVICENAME_MODULEMANAGER ), 104 css::uno::UNO_QUERY_THROW ); 105 106 /*Attention 107 I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance() 108 to create a new instance of this class by our own supported service factory. 109 see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further informations! 110 */ 111 // read the list of all currently registered events inside configuration. 112 // e.g. "/org.openoffice.Office.Jobs/Events/<event name>" 113 // We need it later to check if an incoming event request can be executed successfully 114 // or must be rejected. It's an optimization! Of course we must implement updating of this 115 // list too ... Be listener at the configuration. 116 117 m_aConfig.open(ConfigAccess::E_READONLY); 118 if (m_aConfig.getMode() == ConfigAccess::E_READONLY) 119 { 120 css::uno::Reference< css::container::XNameAccess > xRegistry(m_aConfig.cfg(), css::uno::UNO_QUERY); 121 if (xRegistry.is()) 122 m_lEvents = Converter::convert_seqOUString2OUStringList(xRegistry->getElementNames()); 123 124 css::uno::Reference< css::container::XContainer > xNotifier(m_aConfig.cfg(), css::uno::UNO_QUERY); 125 if (xNotifier.is()) 126 { 127 css::uno::Reference< css::container::XContainerListener > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY); 128 xNotifier->addContainerListener(xThis); 129 } 130 131 // don't close cfg here! 132 // It will be done inside disposing ... 133 } 134 } 135 ) 136 137 //________________________________ 138 139 /** 140 @short standard ctor 141 @descr It initialize this new instance. 142 143 @param xSMGR 144 reference to the uno service manager 145 */ 146 JobExecutor::JobExecutor( /*IN*/ const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR ) 147 : ThreadHelpBase (&Application::GetSolarMutex() ) 148 , ::cppu::OWeakObject ( ) 149 , m_xSMGR (xSMGR ) 150 , m_xModuleManager ( ) 151 , m_aConfig (xSMGR, ::rtl::OUString::createFromAscii(JobData::EVENTCFG_ROOT) ) 152 { 153 // Don't do any reference related code here! Do it inside special 154 // impl_ method() ... see DEFINE_INIT_SERVICE() macro for further informations. 155 } 156 157 JobExecutor::~JobExecutor() 158 { 159 LOG_ASSERT(m_aConfig.getMode() == ConfigAccess::E_CLOSED, "JobExecutor::~JobExecutor()\nConfiguration don't send dispoing() message!\n") 160 } 161 162 //________________________________ 163 164 /** 165 @short implementation of XJobExecutor interface 166 @descr We use the given event to locate any registered job inside our configuration 167 and execute it. Further we control the lifetime of it and supress 168 shutdown of the office till all jobs was finished. 169 170 @param sEvent 171 is used to locate registered jobs 172 */ 173 void SAL_CALL JobExecutor::trigger( const ::rtl::OUString& sEvent ) throw(css::uno::RuntimeException) 174 { 175 RTL_LOGFILE_CONTEXT(aLog, "fwk (as96863) JobExecutor::trigger()"); 176 177 /* SAFE { */ 178 ReadGuard aReadLock(m_aLock); 179 180 // Optimization! 181 // Check if the given event name exist inside configuration and reject wrong requests. 182 // This optimization supress using of the cfg api for getting event and job descriptions ... 183 if (m_lEvents.find(sEvent) == m_lEvents.end()) 184 return; 185 186 // get list of all enabled jobs 187 // The called static helper methods read it from the configuration and 188 // filter disabled jobs using it's time stamp values. 189 css::uno::Sequence< ::rtl::OUString > lJobs = JobData::getEnabledJobsForEvent(m_xSMGR, sEvent); 190 191 aReadLock.unlock(); 192 /* } SAFE */ 193 194 // step over all enabled jobs and execute it 195 sal_Int32 c = lJobs.getLength(); 196 for (sal_Int32 j=0; j<c; ++j) 197 { 198 /* SAFE { */ 199 aReadLock.lock(); 200 201 JobData aCfg(m_xSMGR); 202 aCfg.setEvent(sEvent, lJobs[j]); 203 aCfg.setEnvironment(JobData::E_EXECUTION); 204 205 /*Attention! 206 Jobs implements interfaces and dies by ref count! 207 And freeing of such uno object is done by uno itself. 208 So we have to use dynamic memory everytimes. 209 */ 210 Job* pJob = new Job(m_xSMGR, css::uno::Reference< css::frame::XFrame >()); 211 css::uno::Reference< css::uno::XInterface > xJob(static_cast< ::cppu::OWeakObject* >(pJob), css::uno::UNO_QUERY); 212 pJob->setJobData(aCfg); 213 214 aReadLock.unlock(); 215 /* } SAFE */ 216 217 pJob->execute(css::uno::Sequence< css::beans::NamedValue >()); 218 } 219 } 220 221 //________________________________ 222 223 void SAL_CALL JobExecutor::notifyEvent( const css::document::EventObject& aEvent ) throw(css::uno::RuntimeException) 224 { 225 static ::rtl::OUString EVENT_ON_NEW = DECLARE_ASCII("OnNew" ); // Doc UI event 226 static ::rtl::OUString EVENT_ON_LOAD = DECLARE_ASCII("OnLoad" ); // Doc UI event 227 static ::rtl::OUString EVENT_ON_CREATE = DECLARE_ASCII("OnCreate" ); // Doc API event 228 static ::rtl::OUString EVENT_ON_LOAD_FINISHED = DECLARE_ASCII("OnLoadFinished" ); // Doc API event 229 static ::rtl::OUString EVENT_ON_DOCUMENT_OPENED = DECLARE_ASCII("onDocumentOpened" ); // Job UI event : OnNew or OnLoad 230 static ::rtl::OUString EVENT_ON_DOCUMENT_ADDED = DECLARE_ASCII("onDocumentAdded" ); // Job API event : OnCreate or OnLoadFinished 231 232 /* SAFE { */ 233 ReadGuard aReadLock(m_aLock); 234 235 ::comphelper::SequenceAsVector< JobData::TJob2DocEventBinding > lJobs; 236 237 // Optimization! 238 // Check if the given event name exist inside configuration and reject wrong requests. 239 // This optimization supress using of the cfg api for getting event and job descriptions. 240 // see using of m_lEvents.find() below ... 241 242 // retrieve event context from event source 243 rtl::OUString aModuleIdentifier; 244 try 245 { 246 aModuleIdentifier = m_xModuleManager->identify( aEvent.Source ); 247 } 248 catch( css::uno::Exception& ) 249 {} 250 251 // Special feature: If the events "OnNew" or "OnLoad" occures - we generate our own event "onDocumentOpened". 252 if ( 253 (aEvent.EventName.equals(EVENT_ON_NEW )) || 254 (aEvent.EventName.equals(EVENT_ON_LOAD)) 255 ) 256 { 257 if (m_lEvents.find(EVENT_ON_DOCUMENT_OPENED) != m_lEvents.end()) 258 JobData::appendEnabledJobsForEvent(m_xSMGR, EVENT_ON_DOCUMENT_OPENED, lJobs); 259 } 260 261 // Special feature: If the events "OnCreate" or "OnLoadFinished" occures - we generate our own event "onDocumentAdded". 262 if ( 263 (aEvent.EventName.equals(EVENT_ON_CREATE )) || 264 (aEvent.EventName.equals(EVENT_ON_LOAD_FINISHED)) 265 ) 266 { 267 if (m_lEvents.find(EVENT_ON_DOCUMENT_ADDED) != m_lEvents.end()) 268 JobData::appendEnabledJobsForEvent(m_xSMGR, EVENT_ON_DOCUMENT_ADDED, lJobs); 269 } 270 271 // Add all jobs for "real" notified event too .-) 272 if (m_lEvents.find(aEvent.EventName) != m_lEvents.end()) 273 JobData::appendEnabledJobsForEvent(m_xSMGR, aEvent.EventName, lJobs); 274 275 aReadLock.unlock(); 276 /* } SAFE */ 277 278 // step over all enabled jobs and execute it 279 ::comphelper::SequenceAsVector< JobData::TJob2DocEventBinding >::const_iterator pIt; 280 for ( pIt = lJobs.begin(); 281 pIt != lJobs.end() ; 282 ++pIt ) 283 { 284 /* SAFE { */ 285 aReadLock.lock(); 286 287 const JobData::TJob2DocEventBinding& rBinding = *pIt; 288 289 JobData aCfg(m_xSMGR); 290 aCfg.setEvent(rBinding.m_sDocEvent, rBinding.m_sJobName); 291 aCfg.setEnvironment(JobData::E_DOCUMENTEVENT); 292 293 if (!aCfg.hasCorrectContext(aModuleIdentifier)) 294 continue; 295 296 /*Attention! 297 Jobs implements interfaces and dies by ref count! 298 And freeing of such uno object is done by uno itself. 299 So we have to use dynamic memory everytimes. 300 */ 301 css::uno::Reference< css::frame::XModel > xModel(aEvent.Source, css::uno::UNO_QUERY); 302 Job* pJob = new Job(m_xSMGR, xModel); 303 css::uno::Reference< css::uno::XInterface > xJob(static_cast< ::cppu::OWeakObject* >(pJob), css::uno::UNO_QUERY); 304 pJob->setJobData(aCfg); 305 306 aReadLock.unlock(); 307 /* } SAFE */ 308 309 pJob->execute(css::uno::Sequence< css::beans::NamedValue >()); 310 } 311 } 312 313 //________________________________ 314 315 void SAL_CALL JobExecutor::elementInserted( const css::container::ContainerEvent& aEvent ) throw(css::uno::RuntimeException) 316 { 317 ::rtl::OUString sValue; 318 if (aEvent.Accessor >>= sValue) 319 { 320 ::rtl::OUString sEvent = ::utl::extractFirstFromConfigurationPath(sValue); 321 if (sEvent.getLength() > 0) 322 { 323 OUStringList::iterator pEvent = m_lEvents.find(sEvent); 324 if (pEvent == m_lEvents.end()) 325 m_lEvents.push_back(sEvent); 326 } 327 } 328 } 329 330 void SAL_CALL JobExecutor::elementRemoved ( const css::container::ContainerEvent& aEvent ) throw(css::uno::RuntimeException) 331 { 332 ::rtl::OUString sValue; 333 if (aEvent.Accessor >>= sValue) 334 { 335 ::rtl::OUString sEvent = ::utl::extractFirstFromConfigurationPath(sValue); 336 if (sEvent.getLength() > 0) 337 { 338 OUStringList::iterator pEvent = m_lEvents.find(sEvent); 339 if (pEvent != m_lEvents.end()) 340 m_lEvents.erase(pEvent); 341 } 342 } 343 } 344 345 void SAL_CALL JobExecutor::elementReplaced( const css::container::ContainerEvent& ) throw(css::uno::RuntimeException) 346 { 347 // I'm not interested on changed items :-) 348 } 349 350 //________________________________ 351 352 /** @short the used cfg changes notifier wish to be released in its reference. 353 354 @descr We close our internal used configuration instance to 355 free this reference. 356 357 @attention For the special feature "bind global document event broadcaster to job execution" 358 this job executor instance was registered from outside code as 359 css.document.XEventListener. So it can be, that this disposing call comes from 360 the global event broadcaster service. But we don't hold any reference to this service 361 which can or must be released. Because this broadcaster itself is an one instance service 362 too, we can ignore this request. On the other side we must relase our internal CFG 363 reference ... SOLUTION => check the given event source and react only, if it's our internal 364 hold configuration object! 365 */ 366 void SAL_CALL JobExecutor::disposing( const css::lang::EventObject& aEvent ) throw(css::uno::RuntimeException) 367 { 368 /* SAFE { */ 369 ReadGuard aReadLock(m_aLock); 370 css::uno::Reference< css::uno::XInterface > xCFG(m_aConfig.cfg(), css::uno::UNO_QUERY); 371 if ( 372 (xCFG == aEvent.Source ) && 373 (m_aConfig.getMode() != ConfigAccess::E_CLOSED) 374 ) 375 { 376 m_aConfig.close(); 377 } 378 aReadLock.unlock(); 379 /* } SAFE */ 380 } 381 382 } // namespace framework 383