1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <jobs/jobexecutor.hxx>
21 #include <jobs/job.hxx>
22 #include <jobs/joburl.hxx>
24 #include <classes/converter.hxx>
25 #include <threadhelp/transactionguard.hxx>
26 #include <threadhelp/readguard.hxx>
27 #include <threadhelp/writeguard.hxx>
31 #include "helper/mischelper.hxx"
33 #include <com/sun/star/beans/XPropertySet.hpp>
34 #include <com/sun/star/container/XNameAccess.hpp>
35 #include <com/sun/star/container/XContainer.hpp>
36 #include <com/sun/star/frame/ModuleManager.hpp>
38 #include <unotools/configpaths.hxx>
39 #include <rtl/ustrbuf.hxx>
40 #include <vcl/svapp.hxx>
42 #include <rtl/logfile.hxx>
46 DEFINE_XINTERFACE_6( JobExecutor
,
48 DIRECT_INTERFACE(css::lang::XTypeProvider
),
49 DIRECT_INTERFACE(css::lang::XServiceInfo
),
50 DIRECT_INTERFACE(css::task::XJobExecutor
),
51 DIRECT_INTERFACE(css::container::XContainerListener
),
52 DIRECT_INTERFACE(css::document::XEventListener
),
53 DERIVED_INTERFACE(css::lang::XEventListener
,css::document::XEventListener
)
56 DEFINE_XTYPEPROVIDER_6( JobExecutor
,
57 css::lang::XTypeProvider
,
58 css::lang::XServiceInfo
,
59 css::task::XJobExecutor
,
60 css::container::XContainerListener
,
61 css::document::XEventListener
,
62 css::lang::XEventListener
65 DEFINE_XSERVICEINFO_ONEINSTANCESERVICE( JobExecutor
,
67 "com.sun.star.task.JobExecutor",
68 IMPLEMENTATIONNAME_JOBEXECUTOR
71 DEFINE_INIT_SERVICE( JobExecutor
,
73 m_xModuleManager
= css::frame::ModuleManager::create( comphelper::getComponentContext(m_xSMGR
) );
76 I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance()
77 to create a new instance of this class by our own supported service factory.
78 see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further information!
80 // read the list of all currently registered events inside configuration.
81 // e.g. "/org.openoffice.Office.Jobs/Events/<event name>"
82 // We need it later to check if an incoming event request can be executed successfully
83 // or must be rejected. It's an optimization! Of course we must implement updating of this
84 // list too ... Be listener at the configuration.
86 m_aConfig
.open(ConfigAccess::E_READONLY
);
87 if (m_aConfig
.getMode() == ConfigAccess::E_READONLY
)
89 css::uno::Reference
< css::container::XNameAccess
> xRegistry(m_aConfig
.cfg(), css::uno::UNO_QUERY
);
91 m_lEvents
= Converter::convert_seqOUString2OUStringList(xRegistry
->getElementNames());
93 css::uno::Reference
< css::container::XContainer
> xNotifier(m_aConfig
.cfg(), css::uno::UNO_QUERY
);
96 m_xConfigListener
= new WeakContainerListener(this);
97 xNotifier
->addContainerListener(m_xConfigListener
);
100 // don't close cfg here!
101 // It will be done inside disposing ...
106 //________________________________
110 @descr It initialize this new instance.
113 reference to the uno service manager
115 JobExecutor::JobExecutor( /*IN*/ const css::uno::Reference
< css::lang::XMultiServiceFactory
>& xSMGR
)
116 : ThreadHelpBase (&Application::GetSolarMutex() )
117 , ::cppu::OWeakObject ( )
119 , m_xModuleManager ( )
120 , m_aConfig (comphelper::getComponentContext(xSMGR
), OUString::createFromAscii(JobData::EVENTCFG_ROOT
) )
122 // Don't do any reference related code here! Do it inside special
123 // impl_ method() ... see DEFINE_INIT_SERVICE() macro for further information.
126 JobExecutor::~JobExecutor()
128 css::uno::Reference
< css::container::XContainer
> xNotifier(m_aConfig
.cfg(), css::uno::UNO_QUERY
);
130 xNotifier
->removeContainerListener(m_xConfigListener
);
133 //________________________________
136 @short implementation of XJobExecutor interface
137 @descr We use the given event to locate any registered job inside our configuration
138 and execute it. Further we control the lifetime of it and supress
139 shutdown of the office till all jobs was finished.
142 is used to locate registered jobs
144 void SAL_CALL
JobExecutor::trigger( const OUString
& sEvent
) throw(css::uno::RuntimeException
)
146 RTL_LOGFILE_CONTEXT(aLog
, "fwk (as96863) JobExecutor::trigger()");
149 ReadGuard
aReadLock(m_aLock
);
152 // Check if the given event name exist inside configuration and reject wrong requests.
153 // This optimization supress using of the cfg api for getting event and job descriptions ...
154 if (m_lEvents
.find(sEvent
) == m_lEvents
.end())
157 // get list of all enabled jobs
158 // The called static helper methods read it from the configuration and
159 // filter disabled jobs using it's time stamp values.
160 css::uno::Sequence
< OUString
> lJobs
= JobData::getEnabledJobsForEvent(comphelper::getComponentContext(m_xSMGR
), sEvent
);
165 // step over all enabled jobs and execute it
166 sal_Int32 c
= lJobs
.getLength();
167 for (sal_Int32 j
=0; j
<c
; ++j
)
172 JobData
aCfg(comphelper::getComponentContext(m_xSMGR
));
173 aCfg
.setEvent(sEvent
, lJobs
[j
]);
174 aCfg
.setEnvironment(JobData::E_EXECUTION
);
177 Jobs implements interfaces and dies by ref count!
178 And freeing of such uno object is done by uno itself.
179 So we have to use dynamic memory everytimes.
181 Job
* pJob
= new Job(m_xSMGR
, css::uno::Reference
< css::frame::XFrame
>());
182 css::uno::Reference
< css::uno::XInterface
> xJob(static_cast< ::cppu::OWeakObject
* >(pJob
), css::uno::UNO_QUERY
);
183 pJob
->setJobData(aCfg
);
188 pJob
->execute(css::uno::Sequence
< css::beans::NamedValue
>());
192 //________________________________
194 void SAL_CALL
JobExecutor::notifyEvent( const css::document::EventObject
& aEvent
) throw(css::uno::RuntimeException
)
196 const char EVENT_ON_NEW
[] = "OnNew"; // Doc UI event
197 const char EVENT_ON_LOAD
[] = "OnLoad"; // Doc UI event
198 const char EVENT_ON_CREATE
[] = "OnCreate"; // Doc API event
199 const char EVENT_ON_LOAD_FINISHED
[] = "OnLoadFinished"; // Doc API event
200 OUString
EVENT_ON_DOCUMENT_OPENED("onDocumentOpened"); // Job UI event : OnNew or OnLoad
201 OUString
EVENT_ON_DOCUMENT_ADDED("onDocumentAdded"); // Job API event : OnCreate or OnLoadFinished
204 ReadGuard
aReadLock(m_aLock
);
206 ::comphelper::SequenceAsVector
< JobData::TJob2DocEventBinding
> lJobs
;
209 // Check if the given event name exist inside configuration and reject wrong requests.
210 // This optimization supress using of the cfg api for getting event and job descriptions.
211 // see using of m_lEvents.find() below ...
213 // retrieve event context from event source
214 OUString aModuleIdentifier
;
217 aModuleIdentifier
= m_xModuleManager
->identify( aEvent
.Source
);
219 catch( const css::uno::Exception
& )
222 // Special feature: If the events "OnNew" or "OnLoad" occures - we generate our own event "onDocumentOpened".
224 (aEvent
.EventName
.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_NEW
))) ||
225 (aEvent
.EventName
.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_LOAD
)))
228 if (m_lEvents
.find(EVENT_ON_DOCUMENT_OPENED
) != m_lEvents
.end())
229 JobData::appendEnabledJobsForEvent(comphelper::getComponentContext(m_xSMGR
), EVENT_ON_DOCUMENT_OPENED
, lJobs
);
232 // Special feature: If the events "OnCreate" or "OnLoadFinished" occures - we generate our own event "onDocumentAdded".
234 (aEvent
.EventName
.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_CREATE
))) ||
235 (aEvent
.EventName
.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_LOAD_FINISHED
)))
238 if (m_lEvents
.find(EVENT_ON_DOCUMENT_ADDED
) != m_lEvents
.end())
239 JobData::appendEnabledJobsForEvent(comphelper::getComponentContext(m_xSMGR
), EVENT_ON_DOCUMENT_ADDED
, lJobs
);
242 // Add all jobs for "real" notified event too .-)
243 if (m_lEvents
.find(aEvent
.EventName
) != m_lEvents
.end())
244 JobData::appendEnabledJobsForEvent(comphelper::getComponentContext(m_xSMGR
), aEvent
.EventName
, lJobs
);
249 // step over all enabled jobs and execute it
250 ::comphelper::SequenceAsVector
< JobData::TJob2DocEventBinding
>::const_iterator pIt
;
251 for ( pIt
= lJobs
.begin();
258 const JobData::TJob2DocEventBinding
& rBinding
= *pIt
;
260 JobData
aCfg(comphelper::getComponentContext(m_xSMGR
));
261 aCfg
.setEvent(rBinding
.m_sDocEvent
, rBinding
.m_sJobName
);
262 aCfg
.setEnvironment(JobData::E_DOCUMENTEVENT
);
264 if (!aCfg
.hasCorrectContext(aModuleIdentifier
))
268 Jobs implements interfaces and dies by ref count!
269 And freeing of such uno object is done by uno itself.
270 So we have to use dynamic memory everytimes.
272 css::uno::Reference
< css::frame::XModel
> xModel(aEvent
.Source
, css::uno::UNO_QUERY
);
273 Job
* pJob
= new Job(m_xSMGR
, xModel
);
274 css::uno::Reference
< css::uno::XInterface
> xJob(static_cast< ::cppu::OWeakObject
* >(pJob
), css::uno::UNO_QUERY
);
275 pJob
->setJobData(aCfg
);
280 pJob
->execute(css::uno::Sequence
< css::beans::NamedValue
>());
284 //________________________________
286 void SAL_CALL
JobExecutor::elementInserted( const css::container::ContainerEvent
& aEvent
) throw(css::uno::RuntimeException
)
289 if (aEvent
.Accessor
>>= sValue
)
291 OUString sEvent
= ::utl::extractFirstFromConfigurationPath(sValue
);
292 if (!sEvent
.isEmpty())
294 OUStringList::iterator pEvent
= m_lEvents
.find(sEvent
);
295 if (pEvent
== m_lEvents
.end())
296 m_lEvents
.push_back(sEvent
);
301 void SAL_CALL
JobExecutor::elementRemoved ( const css::container::ContainerEvent
& aEvent
) throw(css::uno::RuntimeException
)
304 if (aEvent
.Accessor
>>= sValue
)
306 OUString sEvent
= ::utl::extractFirstFromConfigurationPath(sValue
);
307 if (!sEvent
.isEmpty())
309 OUStringList::iterator pEvent
= m_lEvents
.find(sEvent
);
310 if (pEvent
!= m_lEvents
.end())
311 m_lEvents
.erase(pEvent
);
316 void SAL_CALL
JobExecutor::elementReplaced( const css::container::ContainerEvent
& ) throw(css::uno::RuntimeException
)
318 // I'm not interested on changed items :-)
321 //________________________________
323 /** @short the used cfg changes notifier wish to be released in its reference.
325 @descr We close our internal used configuration instance to
328 @attention For the special feature "bind global document event broadcaster to job execution"
329 this job executor instance was registered from outside code as
330 css.document.XEventListener. So it can be, that this disposing call comes from
331 the global event broadcaster service. But we don't hold any reference to this service
332 which can or must be released. Because this broadcaster itself is an one instance service
333 too, we can ignore this request. On the other side we must relase our internal CFG
334 reference ... SOLUTION => check the given event source and react only, if it's our internal
335 hold configuration object!
337 void SAL_CALL
JobExecutor::disposing( const css::lang::EventObject
& aEvent
) throw(css::uno::RuntimeException
)
340 ReadGuard
aReadLock(m_aLock
);
341 css::uno::Reference
< css::uno::XInterface
> xCFG(m_aConfig
.cfg(), css::uno::UNO_QUERY
);
343 (xCFG
== aEvent
.Source
) &&
344 (m_aConfig
.getMode() != ConfigAccess::E_CLOSED
)
353 } // namespace framework
355 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */