LibreOffice Module framework (master) 1
jobexecutor.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <jobs/job.hxx>
21#include <jobs/configaccess.hxx>
22#include <classes/converter.hxx>
23
24#include <helper/mischelper.hxx>
25
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>
33
38#include <rtl/ref.hxx>
39#include <sal/log.hxx>
40#include <vcl/svapp.hxx>
41
42using namespace framework;
43
44namespace {
45
47 css::lang::XServiceInfo
48 , css::task::XJobExecutor
49 , css::container::XContainerListener // => lang.XEventListener
50 , css::document::XEventListener >
51 Base;
52
59class JobExecutor : public Base
60{
61private:
62
64 css::uno::Reference< css::uno::XComponentContext > m_xContext;
65
67 std::vector<OUString> m_lEvents;
68
70 ConfigAccess m_aConfig;
71
73 css::uno::Reference<css::container::XContainerListener> m_xConfigListener;
74
75 virtual void disposing(std::unique_lock<std::mutex>& rGuard) final override;
76
77public:
78
79 explicit JobExecutor(const css::uno::Reference< css::uno::XComponentContext >& xContext);
80 virtual ~JobExecutor() override;
81
82 virtual OUString SAL_CALL getImplementationName() override
83 {
84 return "com.sun.star.comp.framework.JobExecutor";
85 }
86
87 virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
88 {
89 return cppu::supportsService(this, ServiceName);
90 }
91
92 virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
93 {
94 return {"com.sun.star.task.JobExecutor"};
95 }
96
97 // task.XJobExecutor
98 virtual void SAL_CALL trigger( const OUString& sEvent ) override;
99
101 void initListeners();
102
103 // document.XEventListener
104 virtual void SAL_CALL notifyEvent( const css::document::EventObject& aEvent ) override;
105
106 // container.XContainerListener
107 virtual void SAL_CALL elementInserted( const css::container::ContainerEvent& aEvent ) override;
108 virtual void SAL_CALL elementRemoved ( const css::container::ContainerEvent& aEvent ) override;
109 virtual void SAL_CALL elementReplaced( const css::container::ContainerEvent& aEvent ) override;
110
111 // lang.XEventListener
112 virtual void SAL_CALL disposing( const css::lang::EventObject& aEvent ) override;
113};
114
122JobExecutor::JobExecutor( /*IN*/ const css::uno::Reference< css::uno::XComponentContext >& xContext )
123 : m_xContext (xContext )
124 , m_aConfig (xContext, "/org.openoffice.Office.Jobs/Events")
125{
126}
127
128void JobExecutor::initListeners()
129{
131 return;
132
133 // read the list of all currently registered events inside configuration.
134 // e.g. "/org.openoffice.Office.Jobs/Events/<event name>"
135 // We need it later to check if an incoming event request can be executed successfully
136 // or must be rejected. It's an optimization! Of course we must implement updating of this
137 // list too ... Be listener at the configuration.
138
139 m_aConfig.open(ConfigAccess::E_READONLY);
140 if (m_aConfig.getMode() != ConfigAccess::E_READONLY)
141 return;
142
143 css::uno::Reference< css::container::XNameAccess > xRegistry(
144 m_aConfig.cfg(), css::uno::UNO_QUERY);
145 if (xRegistry.is())
146 m_lEvents = Converter::convert_seqOUString2OUStringList(
147 xRegistry->getElementNames());
148
149 css::uno::Reference< css::container::XContainer > xNotifier(
150 m_aConfig.cfg(), css::uno::UNO_QUERY);
151 if (xNotifier.is())
152 {
154 xNotifier->addContainerListener(m_xConfigListener);
155 }
156
157 // don't close cfg here!
158 // It will be done inside disposing ...
159}
160
161JobExecutor::~JobExecutor()
162{
163 std::unique_lock g(m_aMutex);
164 disposing(g);
165}
166
167void JobExecutor::disposing(std::unique_lock<std::mutex>& /*rGuard*/) {
168 css::uno::Reference<css::container::XContainer> notifier;
169 css::uno::Reference<css::container::XContainerListener> listener;
170 if (m_aConfig.getMode() != ConfigAccess::E_CLOSED) {
171 notifier.set(m_aConfig.cfg(), css::uno::UNO_QUERY);
172 listener = m_xConfigListener;
173 m_aConfig.close();
174 }
175 m_xConfigListener.clear();
176 if (notifier.is()) {
177 notifier->removeContainerListener(listener);
178 }
179}
180
190void SAL_CALL JobExecutor::trigger( const OUString& sEvent )
191{
192 SAL_INFO( "fwk", "JobExecutor::trigger()");
193
194 /* SAFE */
195 {
196 std::unique_lock g(m_aMutex);
197
198 // Optimization!
199 // Check if the given event name exist inside configuration and reject wrong requests.
200 // This optimization suppress using of the cfg api for getting event and job descriptions ...
201 if (std::find(m_lEvents.begin(), m_lEvents.end(), sEvent) == m_lEvents.end())
202 return;
203
204 } /* SAFE */
205
206 // get list of all enabled jobs
207 // The called static helper methods read it from the configuration and
208 // filter disabled jobs using it's time stamp values.
209 std::vector< OUString > lJobs = JobData::getEnabledJobsForEvent(m_xContext, sEvent);
210
211 // step over all enabled jobs and execute it
212 size_t c = lJobs.size();
213 for (size_t j=0; j<c; ++j)
214 {
215 JobData aCfg(m_xContext);
216 aCfg.setEvent(sEvent, lJobs[j]);
217 aCfg.setEnvironment(JobData::E_EXECUTION);
218
219 /*Attention!
220 Jobs implements interfaces and dies by ref count!
221 And freeing of such uno object is done by uno itself.
222 So we have to use dynamic memory everytimes.
223 */
224 rtl::Reference<Job> pJob = new Job(m_xContext, css::uno::Reference< css::frame::XFrame >());
225 pJob->setJobData(aCfg);
226
227 pJob->execute(css::uno::Sequence< css::beans::NamedValue >());
228 }
229}
230
231void SAL_CALL JobExecutor::notifyEvent( const css::document::EventObject& aEvent )
232{
233 static constexpr OUStringLiteral EVENT_ON_DOCUMENT_OPENED(u"onDocumentOpened"); // Job UI event : OnNew or OnLoad
234 static constexpr OUStringLiteral EVENT_ON_DOCUMENT_ADDED(u"onDocumentAdded"); // Job API event : OnCreate or OnLoadFinished
235
236 OUString aModuleIdentifier;
237 ::std::vector< JobData::TJob2DocEventBinding > lJobs;
238
239 // Optimization!
240 // Check if the given event name exist inside configuration and reject wrong requests.
241 // This optimization suppress using of the cfg api for getting event and job descriptions.
242 // see using of m_lEvents.find() below ...
243
244 // retrieve event context from event source
245 try
246 {
247 aModuleIdentifier = css::frame::ModuleManager::create( m_xContext )->identify( aEvent.Source );
248 }
249 catch( const css::uno::Exception& )
250 {}
251
252 /* SAFE */
253 {
254 std::unique_lock g(m_aMutex);
255
256 // Special feature: If the events "OnNew" or "OnLoad" occurs - we generate our own event "onDocumentOpened".
257 if (
258 (aEvent.EventName == "OnNew") ||
259 (aEvent.EventName == "OnLoad")
260 )
261 {
262 if (std::find(m_lEvents.begin(), m_lEvents.end(), EVENT_ON_DOCUMENT_OPENED) != m_lEvents.end())
263 JobData::appendEnabledJobsForEvent(m_xContext, EVENT_ON_DOCUMENT_OPENED, lJobs);
264 }
265
266 // Special feature: If the events "OnCreate" or "OnLoadFinished" occurs - we generate our own event "onDocumentAdded".
267 if (
268 (aEvent.EventName == "OnCreate") ||
269 (aEvent.EventName == "OnLoadFinished")
270 )
271 {
272 if (std::find(m_lEvents.begin(), m_lEvents.end(), EVENT_ON_DOCUMENT_ADDED) != m_lEvents.end())
273 JobData::appendEnabledJobsForEvent(m_xContext, EVENT_ON_DOCUMENT_ADDED, lJobs);
274 }
275
276 // Add all jobs for "real" notified event too .-)
277 if (std::find(m_lEvents.begin(), m_lEvents.end(), aEvent.EventName) != m_lEvents.end())
278 JobData::appendEnabledJobsForEvent(m_xContext, aEvent.EventName, lJobs);
279 } /* SAFE */
280
281 // step over all enabled jobs and execute it
282 for (auto const& lJob : lJobs)
283 {
285
286 const JobData::TJob2DocEventBinding& rBinding = lJob;
287
288 JobData aCfg(m_xContext);
289 aCfg.setEvent(rBinding.m_sDocEvent, rBinding.m_sJobName);
290 aCfg.setEnvironment(JobData::E_DOCUMENTEVENT);
291
292 if (!aCfg.hasCorrectContext(aModuleIdentifier))
293 continue;
294
295 /*Attention!
296 Jobs implements interfaces and dies by ref count!
297 And freeing of such uno object is done by uno itself.
298 So we have to use dynamic memory everytimes.
299 */
300 css::uno::Reference< css::frame::XModel > xModel(aEvent.Source, css::uno::UNO_QUERY);
301 pJob = new Job(m_xContext, xModel);
302 pJob->setJobData(aCfg);
303
304 pJob->execute(css::uno::Sequence< css::beans::NamedValue >());
305 }
306}
307
308void SAL_CALL JobExecutor::elementInserted( const css::container::ContainerEvent& aEvent )
309{
310 OUString sValue;
311 if (aEvent.Accessor >>= sValue)
312 {
313 OUString sEvent = ::utl::extractFirstFromConfigurationPath(sValue);
314 if (!sEvent.isEmpty())
315 {
316 std::vector<OUString>::iterator pEvent = std::find(m_lEvents.begin(), m_lEvents.end(), sEvent);
317 if (pEvent == m_lEvents.end())
318 m_lEvents.push_back(sEvent);
319 }
320 }
321}
322
323void SAL_CALL JobExecutor::elementRemoved ( const css::container::ContainerEvent& aEvent )
324{
325 OUString sValue;
326 if (aEvent.Accessor >>= sValue)
327 {
328 OUString sEvent = ::utl::extractFirstFromConfigurationPath(sValue);
329 if (!sEvent.isEmpty())
330 {
331 std::vector<OUString>::iterator pEvent = std::find(m_lEvents.begin(), m_lEvents.end(), sEvent);
332 if (pEvent != m_lEvents.end())
333 m_lEvents.erase(pEvent);
334 }
335 }
336}
337
338void SAL_CALL JobExecutor::elementReplaced( const css::container::ContainerEvent& )
339{
340 // I'm not interested on changed items :-)
341}
342
357void SAL_CALL JobExecutor::disposing( const css::lang::EventObject& aEvent )
358{
359 /* SAFE { */
360 std::unique_lock g(m_aMutex);
361 css::uno::Reference< css::uno::XInterface > xCFG(m_aConfig.cfg(), css::uno::UNO_QUERY);
362 if (
363 (xCFG == aEvent.Source ) &&
364 (m_aConfig.getMode() != ConfigAccess::E_CLOSED)
365 )
366 {
367 m_aConfig.close();
368 }
369 /* } SAFE */
370}
371
372}
373
374extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
376 css::uno::XComponentContext *context,
377 css::uno::Sequence<css::uno::Any> const &)
378{
379 rtl::Reference<JobExecutor> xJobExec = new JobExecutor(context);
380 // 2nd phase initialization needed
381 xJobExec->initListeners();
382 return cppu::acquire(xJobExec.get());
383}
384
385/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
AnyEventRef aEvent
implements a simple configuration access @descr Sometimes it's better to have direct config access in...
holds all necessary information about a job and handle it's configuration (if any exist!...
Definition: jobdata.hxx:41
it represent a job; execute it and control its lifetime
Definition: job.hxx:46
static bool IsFuzzing()
css::uno::Reference< css::uno::XComponentContext > m_xContext
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * com_sun_star_comp_framework_JobExecutor_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &)
void SAL_CALL elementReplaced(const css::container::ContainerEvent &Event) override
void SAL_CALL elementRemoved(const css::container::ContainerEvent &Event) override
DECL_LISTENERMULTIPLEXER_END void SAL_CALL elementInserted(const css::container::ContainerEvent &Event) override
#define SAL_INFO(area, stream)
css::uno::Sequence< OUString > getSupportedServiceNames()
OUString getImplementationName()
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
Some jobs can be registered to "logical events", which are generated on demand if another document ev...
Definition: jobdata.hxx:86
Reference< XModel > xModel
unsigned char sal_Bool
Base
std::mutex m_aMutex
Reference< XContainerListener > m_xConfigListener