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/job.hxx>
21 #include <jobs/configaccess.hxx>
22 #include <classes/converter.hxx>
24 #include <helper/mischelper.hxx>
26 #include <com/sun/star/container/XNameAccess.hpp>
27 #include <com/sun/star/container/XContainer.hpp>
28 #include <com/sun/star/frame/ModuleManager.hpp>
29 #include <com/sun/star/task/XJobExecutor.hpp>
30 #include <com/sun/star/container/XContainerListener.hpp>
31 #include <com/sun/star/lang/XServiceInfo.hpp>
32 #include <com/sun/star/document/XEventListener.hpp>
34 #include <cppuhelper/basemutex.hxx>
35 #include <cppuhelper/compbase.hxx>
36 #include <cppuhelper/supportsservice.hxx>
37 #include <unotools/configmgr.hxx>
38 #include <unotools/configpaths.hxx>
39 #include <rtl/ref.hxx>
40 #include <sal/log.hxx>
41 #include <vcl/svapp.hxx>
43 using namespace framework
;
47 typedef cppu::WeakComponentImplHelper
<
48 css::lang::XServiceInfo
49 , css::task::XJobExecutor
50 , css::container::XContainerListener
// => lang.XEventListener
51 , css::document::XEventListener
>
55 @short implements a job executor, which can be triggered from any code
56 @descr It uses the given trigger event to locate any registered job service
57 inside the configuration and execute it. Of course it controls the
58 lifetime of such jobs too.
60 class JobExecutor
: private cppu::BaseMutex
, public Base
64 /** reference to the uno service manager */
65 css::uno::Reference
< css::uno::XComponentContext
> m_xContext
;
67 /** cached list of all registered event names of cfg for call optimization. */
68 std::vector
<OUString
> m_lEvents
;
70 /** we listen at the configuration for changes at the event list. */
71 ConfigAccess m_aConfig
;
73 /** helper to allow us listen to the configuration without a cyclic dependency */
74 css::uno::Reference
<css::container::XContainerListener
> m_xConfigListener
;
76 virtual void SAL_CALL
disposing() final override
;
80 explicit JobExecutor(const css::uno::Reference
< css::uno::XComponentContext
>& xContext
);
81 virtual ~JobExecutor() override
;
83 virtual OUString SAL_CALL
getImplementationName() override
85 return "com.sun.star.comp.framework.JobExecutor";
88 virtual sal_Bool SAL_CALL
supportsService(OUString
const & ServiceName
) override
90 return cppu::supportsService(this, ServiceName
);
93 virtual css::uno::Sequence
<OUString
> SAL_CALL
getSupportedServiceNames() override
95 return {"com.sun.star.task.JobExecutor"};
99 virtual void SAL_CALL
trigger( const OUString
& sEvent
) override
;
101 /// Initialization function after having acquire()'d.
102 void initListeners();
104 // document.XEventListener
105 virtual void SAL_CALL
notifyEvent( const css::document::EventObject
& aEvent
) override
;
107 // container.XContainerListener
108 virtual void SAL_CALL
elementInserted( const css::container::ContainerEvent
& aEvent
) override
;
109 virtual void SAL_CALL
elementRemoved ( const css::container::ContainerEvent
& aEvent
) override
;
110 virtual void SAL_CALL
elementReplaced( const css::container::ContainerEvent
& aEvent
) override
;
112 // lang.XEventListener
113 virtual void SAL_CALL
disposing( const css::lang::EventObject
& aEvent
) override
;
118 @descr It initialize this new instance.
121 reference to the uno service manager
123 JobExecutor::JobExecutor( /*IN*/ const css::uno::Reference
< css::uno::XComponentContext
>& xContext
)
125 , m_xContext (xContext
)
126 , m_aConfig (xContext
, "/org.openoffice.Office.Jobs/Events")
130 void JobExecutor::initListeners()
132 if (utl::ConfigManager::IsFuzzing())
135 // read the list of all currently registered events inside configuration.
136 // e.g. "/org.openoffice.Office.Jobs/Events/<event name>"
137 // We need it later to check if an incoming event request can be executed successfully
138 // or must be rejected. It's an optimization! Of course we must implement updating of this
139 // list too ... Be listener at the configuration.
141 m_aConfig
.open(ConfigAccess::E_READONLY
);
142 if (m_aConfig
.getMode() != ConfigAccess::E_READONLY
)
145 css::uno::Reference
< css::container::XNameAccess
> xRegistry(
146 m_aConfig
.cfg(), css::uno::UNO_QUERY
);
148 m_lEvents
= Converter::convert_seqOUString2OUStringList(
149 xRegistry
->getElementNames());
151 css::uno::Reference
< css::container::XContainer
> xNotifier(
152 m_aConfig
.cfg(), css::uno::UNO_QUERY
);
155 m_xConfigListener
= new WeakContainerListener(this);
156 xNotifier
->addContainerListener(m_xConfigListener
);
159 // don't close cfg here!
160 // It will be done inside disposing ...
163 JobExecutor::~JobExecutor()
168 void JobExecutor::disposing() {
169 css::uno::Reference
<css::container::XContainer
> notifier
;
170 css::uno::Reference
<css::container::XContainerListener
> listener
;
172 osl::MutexGuard
g(rBHelper
.rMutex
);
173 if (m_aConfig
.getMode() != ConfigAccess::E_CLOSED
) {
174 notifier
.set(m_aConfig
.cfg(), css::uno::UNO_QUERY
);
175 listener
= m_xConfigListener
;
178 m_xConfigListener
.clear();
181 notifier
->removeContainerListener(listener
);
186 @short implementation of XJobExecutor interface
187 @descr We use the given event to locate any registered job inside our configuration
188 and execute it. Further we control the lifetime of it and suppress
189 shutdown of the office till all jobs was finished.
192 is used to locate registered jobs
194 void SAL_CALL
JobExecutor::trigger( const OUString
& sEvent
)
196 SAL_INFO( "fwk", "JobExecutor::trigger()");
198 std::vector
< OUString
> lJobs
;
201 osl::MutexGuard
g(rBHelper
.rMutex
);
204 // Check if the given event name exist inside configuration and reject wrong requests.
205 // This optimization suppress using of the cfg api for getting event and job descriptions ...
206 if (std::find(m_lEvents
.begin(), m_lEvents
.end(), sEvent
) == m_lEvents
.end())
209 // get list of all enabled jobs
210 // The called static helper methods read it from the configuration and
211 // filter disabled jobs using it's time stamp values.
212 lJobs
= JobData::getEnabledJobsForEvent(m_xContext
, sEvent
);
215 // step over all enabled jobs and execute it
216 size_t c
= lJobs
.size();
217 for (size_t j
=0; j
<c
; ++j
)
219 rtl::Reference
<Job
> pJob
;
225 JobData
aCfg(m_xContext
);
226 aCfg
.setEvent(sEvent
, lJobs
[j
]);
227 aCfg
.setEnvironment(JobData::E_EXECUTION
);
230 Jobs implements interfaces and dies by ref count!
231 And freeing of such uno object is done by uno itself.
232 So we have to use dynamic memory everytimes.
234 pJob
= new Job(m_xContext
, css::uno::Reference
< css::frame::XFrame
>());
235 pJob
->setJobData(aCfg
);
238 pJob
->execute(css::uno::Sequence
< css::beans::NamedValue
>());
242 void SAL_CALL
JobExecutor::notifyEvent( const css::document::EventObject
& aEvent
)
244 static const OUStringLiteral
EVENT_ON_DOCUMENT_OPENED(u
"onDocumentOpened"); // Job UI event : OnNew or OnLoad
245 static const OUStringLiteral
EVENT_ON_DOCUMENT_ADDED(u
"onDocumentAdded"); // Job API event : OnCreate or OnLoadFinished
247 OUString aModuleIdentifier
;
248 ::std::vector
< JobData::TJob2DocEventBinding
> lJobs
;
251 osl::MutexGuard
g(rBHelper
.rMutex
);
254 // Check if the given event name exist inside configuration and reject wrong requests.
255 // This optimization suppress using of the cfg api for getting event and job descriptions.
256 // see using of m_lEvents.find() below ...
258 // retrieve event context from event source
261 aModuleIdentifier
= css::frame::ModuleManager::create( m_xContext
)->identify( aEvent
.Source
);
263 catch( const css::uno::Exception
& )
266 // Special feature: If the events "OnNew" or "OnLoad" occurs - we generate our own event "onDocumentOpened".
268 (aEvent
.EventName
== "OnNew") ||
269 (aEvent
.EventName
== "OnLoad")
272 if (std::find(m_lEvents
.begin(), m_lEvents
.end(), EVENT_ON_DOCUMENT_OPENED
) != m_lEvents
.end())
273 JobData::appendEnabledJobsForEvent(m_xContext
, EVENT_ON_DOCUMENT_OPENED
, lJobs
);
276 // Special feature: If the events "OnCreate" or "OnLoadFinished" occurs - we generate our own event "onDocumentAdded".
278 (aEvent
.EventName
== "OnCreate") ||
279 (aEvent
.EventName
== "OnLoadFinished")
282 if (std::find(m_lEvents
.begin(), m_lEvents
.end(), EVENT_ON_DOCUMENT_ADDED
) != m_lEvents
.end())
283 JobData::appendEnabledJobsForEvent(m_xContext
, EVENT_ON_DOCUMENT_ADDED
, lJobs
);
286 // Add all jobs for "real" notified event too .-)
287 if (std::find(m_lEvents
.begin(), m_lEvents
.end(), aEvent
.EventName
) != m_lEvents
.end())
288 JobData::appendEnabledJobsForEvent(m_xContext
, aEvent
.EventName
, lJobs
);
291 // step over all enabled jobs and execute it
292 for (auto const& lJob
: lJobs
)
294 rtl::Reference
<Job
> pJob
;
299 const JobData::TJob2DocEventBinding
& rBinding
= lJob
;
301 JobData
aCfg(m_xContext
);
302 aCfg
.setEvent(rBinding
.m_sDocEvent
, rBinding
.m_sJobName
);
303 aCfg
.setEnvironment(JobData::E_DOCUMENTEVENT
);
305 if (!aCfg
.hasCorrectContext(aModuleIdentifier
))
309 Jobs implements interfaces and dies by ref count!
310 And freeing of such uno object is done by uno itself.
311 So we have to use dynamic memory everytimes.
313 css::uno::Reference
< css::frame::XModel
> xModel(aEvent
.Source
, css::uno::UNO_QUERY
);
314 pJob
= new Job(m_xContext
, xModel
);
315 pJob
->setJobData(aCfg
);
318 pJob
->execute(css::uno::Sequence
< css::beans::NamedValue
>());
322 void SAL_CALL
JobExecutor::elementInserted( const css::container::ContainerEvent
& aEvent
)
325 if (aEvent
.Accessor
>>= sValue
)
327 OUString sEvent
= ::utl::extractFirstFromConfigurationPath(sValue
);
328 if (!sEvent
.isEmpty())
330 std::vector
<OUString
>::iterator pEvent
= std::find(m_lEvents
.begin(), m_lEvents
.end(), sEvent
);
331 if (pEvent
== m_lEvents
.end())
332 m_lEvents
.push_back(sEvent
);
337 void SAL_CALL
JobExecutor::elementRemoved ( const css::container::ContainerEvent
& aEvent
)
340 if (aEvent
.Accessor
>>= sValue
)
342 OUString sEvent
= ::utl::extractFirstFromConfigurationPath(sValue
);
343 if (!sEvent
.isEmpty())
345 std::vector
<OUString
>::iterator pEvent
= std::find(m_lEvents
.begin(), m_lEvents
.end(), sEvent
);
346 if (pEvent
!= m_lEvents
.end())
347 m_lEvents
.erase(pEvent
);
352 void SAL_CALL
JobExecutor::elementReplaced( const css::container::ContainerEvent
& )
354 // I'm not interested on changed items :-)
357 /** @short the used cfg changes notifier wish to be released in its reference.
359 @descr We close our internal used configuration instance to
362 @attention For the special feature "bind global document event broadcaster to job execution"
363 this job executor instance was registered from outside code as
364 css.document.XEventListener. So it can be, that this disposing call comes from
365 the global event broadcaster service. But we don't hold any reference to this service
366 which can or must be released. Because this broadcaster itself is a one instance service
367 too, we can ignore this request. On the other side we must release our internal CFG
368 reference... SOLUTION => check the given event source and react only, if it's our internal
369 hold configuration object!
371 void SAL_CALL
JobExecutor::disposing( const css::lang::EventObject
& aEvent
)
374 osl::MutexGuard
g(rBHelper
.rMutex
);
375 css::uno::Reference
< css::uno::XInterface
> xCFG(m_aConfig
.cfg(), css::uno::UNO_QUERY
);
377 (xCFG
== aEvent
.Source
) &&
378 (m_aConfig
.getMode() != ConfigAccess::E_CLOSED
)
388 css::uno::Reference
<css::uno::XComponentContext
> const & context
):
390 static_cast<cppu::OWeakObject
*>(new JobExecutor(context
)))
392 // 2nd phase initialization needed
393 static_cast<JobExecutor
*>(static_cast<cppu::OWeakObject
*>
394 (instance
.get()))->initListeners();
397 rtl::Reference
<css::uno::XInterface
> instance
;
401 public rtl::StaticWithArg
<
402 Instance
, css::uno::Reference
<css::uno::XComponentContext
>, Singleton
>
407 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
408 com_sun_star_comp_framework_JobExecutor_get_implementation(
409 css::uno::XComponentContext
*context
,
410 css::uno::Sequence
<css::uno::Any
> const &)
412 return cppu::acquire(static_cast<cppu::OWeakObject
*>(
413 Singleton::get(context
).instance
.get()));
416 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */