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