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/joburl.hxx>
22 #include <jobs/configaccess.hxx>
23 #include <classes/converter.hxx>
27 #include "helper/mischelper.hxx"
29 #include <com/sun/star/beans/XPropertySet.hpp>
30 #include <com/sun/star/container/XNameAccess.hpp>
31 #include <com/sun/star/container/XContainer.hpp>
32 #include <com/sun/star/frame/ModuleManager.hpp>
33 #include <com/sun/star/task/XJobExecutor.hpp>
34 #include <com/sun/star/container/XContainerListener.hpp>
35 #include <com/sun/star/lang/XEventListener.hpp>
36 #include <com/sun/star/lang/XServiceInfo.hpp>
37 #include <com/sun/star/document/XEventListener.hpp>
38 #include <com/sun/star/frame/XModuleManager2.hpp>
40 #include <cppuhelper/basemutex.hxx>
41 #include <cppuhelper/compbase4.hxx>
42 #include <cppuhelper/supportsservice.hxx>
43 #include <unotools/configpaths.hxx>
44 #include <rtl/ref.hxx>
45 #include <rtl/ustrbuf.hxx>
46 #include <vcl/svapp.hxx>
48 using namespace framework
;
52 typedef cppu::WeakComponentImplHelper4
<
53 css::lang::XServiceInfo
54 , css::task::XJobExecutor
55 , css::container::XContainerListener
// => lang.XEventListener
56 , css::document::XEventListener
>
60 @short implements a job executor, which can be triggered from any code
61 @descr It uses the given trigger event to locate any registered job service
62 inside the configuration and execute it. Of course it controls the
63 lifetime of such jobs too.
65 class JobExecutor
: private cppu::BaseMutex
, public Base
69 /** reference to the uno service manager */
70 css::uno::Reference
< css::uno::XComponentContext
> m_xContext
;
72 /** cached list of all registered event names of cfg for call optimization. */
73 OUStringList m_lEvents
;
75 /** we listen at the configuration for changes at the event list. */
76 ConfigAccess m_aConfig
;
78 /** helper to allow us listen to the configuration without a cyclic dependency */
79 com::sun::star::uno::Reference
<com::sun::star::container::XContainerListener
> m_xConfigListener
;
81 virtual void SAL_CALL
disposing() SAL_OVERRIDE
;
85 JobExecutor( const css::uno::Reference
< css::uno::XComponentContext
>& xContext
);
86 virtual ~JobExecutor();
88 virtual OUString SAL_CALL
getImplementationName()
89 throw (css::uno::RuntimeException
, std::exception
) SAL_OVERRIDE
91 return OUString("com.sun.star.comp.framework.JobExecutor");
94 virtual sal_Bool SAL_CALL
supportsService(OUString
const & ServiceName
)
95 throw (css::uno::RuntimeException
, std::exception
) SAL_OVERRIDE
97 return cppu::supportsService(this, ServiceName
);
100 virtual css::uno::Sequence
<OUString
> SAL_CALL
getSupportedServiceNames()
101 throw (css::uno::RuntimeException
, std::exception
) SAL_OVERRIDE
103 css::uno::Sequence
< OUString
> aSeq(1);
104 aSeq
[0] = "com.sun.star.task.JobExecutor";
109 virtual void SAL_CALL
trigger( const OUString
& sEvent
) throw(css::uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
111 /// Initialization function after having acquire()'d.
112 void initListeners();
114 // document.XEventListener
115 virtual void SAL_CALL
notifyEvent( const css::document::EventObject
& aEvent
) throw(css::uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
117 // container.XContainerListener
118 virtual void SAL_CALL
elementInserted( const css::container::ContainerEvent
& aEvent
) throw(css::uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
119 virtual void SAL_CALL
elementRemoved ( const css::container::ContainerEvent
& aEvent
) throw(css::uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
120 virtual void SAL_CALL
elementReplaced( const css::container::ContainerEvent
& aEvent
) throw(css::uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
122 // lang.XEventListener
123 virtual void SAL_CALL
disposing( const css::lang::EventObject
& aEvent
) throw(css::uno::RuntimeException
, std::exception
) SAL_OVERRIDE
;
128 @descr It initialize this new instance.
131 reference to the uno service manager
133 JobExecutor::JobExecutor( /*IN*/ const css::uno::Reference
< css::uno::XComponentContext
>& xContext
)
135 , m_xContext (xContext
)
136 , m_aConfig (xContext
, "/org.openoffice.Office.Jobs/Events")
140 void JobExecutor::initListeners()
142 // read the list of all currently registered events inside configuration.
143 // e.g. "/org.openoffice.Office.Jobs/Events/<event name>"
144 // We need it later to check if an incoming event request can be executed successfully
145 // or must be rejected. It's an optimization! Of course we must implement updating of this
146 // list too ... Be listener at the configuration.
148 m_aConfig
.open(ConfigAccess::E_READONLY
);
149 if (m_aConfig
.getMode() == ConfigAccess::E_READONLY
)
151 css::uno::Reference
< css::container::XNameAccess
> xRegistry(
152 m_aConfig
.cfg(), css::uno::UNO_QUERY
);
154 m_lEvents
= Converter::convert_seqOUString2OUStringList(
155 xRegistry
->getElementNames());
157 css::uno::Reference
< css::container::XContainer
> xNotifier(
158 m_aConfig
.cfg(), css::uno::UNO_QUERY
);
161 m_xConfigListener
= new WeakContainerListener(this);
162 xNotifier
->addContainerListener(m_xConfigListener
);
165 // don't close cfg here!
166 // It will be done inside disposing ...
170 JobExecutor::~JobExecutor()
175 void JobExecutor::disposing() {
176 css::uno::Reference
<css::container::XContainer
> notifier
;
177 css::uno::Reference
<css::container::XContainerListener
> listener
;
179 osl::MutexGuard
g(rBHelper
.rMutex
);
180 if (m_aConfig
.getMode() != ConfigAccess::E_CLOSED
) {
181 notifier
.set(m_aConfig
.cfg(), css::uno::UNO_QUERY
);
182 listener
= m_xConfigListener
;
185 m_xConfigListener
.clear();
188 notifier
->removeContainerListener(listener
);
193 @short implementation of XJobExecutor interface
194 @descr We use the given event to locate any registered job inside our configuration
195 and execute it. Further we control the lifetime of it and suppress
196 shutdown of the office till all jobs was finished.
199 is used to locate registered jobs
201 void SAL_CALL
JobExecutor::trigger( const OUString
& sEvent
) throw(css::uno::RuntimeException
, std::exception
)
203 SAL_INFO( "fwk", "JobExecutor::trigger()");
205 css::uno::Sequence
< OUString
> lJobs
;
208 osl::MutexGuard
g(rBHelper
.rMutex
);
211 // Check if the given event name exist inside configuration and reject wrong requests.
212 // This optimization suppress using of the cfg api for getting event and job descriptions ...
213 if (framework::find(m_lEvents
, sEvent
) == m_lEvents
.end())
216 // get list of all enabled jobs
217 // The called static helper methods read it from the configuration and
218 // filter disabled jobs using it's time stamp values.
219 lJobs
= JobData::getEnabledJobsForEvent(m_xContext
, sEvent
);
222 // step over all enabled jobs and execute it
223 sal_Int32 c
= lJobs
.getLength();
224 for (sal_Int32 j
=0; j
<c
; ++j
)
226 rtl::Reference
<Job
> pJob
;
231 JobData
aCfg(m_xContext
);
232 aCfg
.setEvent(sEvent
, lJobs
[j
]);
233 aCfg
.setEnvironment(JobData::E_EXECUTION
);
236 Jobs implements interfaces and dies by ref count!
237 And freeing of such uno object is done by uno itself.
238 So we have to use dynamic memory everytimes.
240 pJob
= new Job(m_xContext
, css::uno::Reference
< css::frame::XFrame
>());
241 pJob
->setJobData(aCfg
);
244 pJob
->execute(css::uno::Sequence
< css::beans::NamedValue
>());
248 void SAL_CALL
JobExecutor::notifyEvent( const css::document::EventObject
& aEvent
) throw(css::uno::RuntimeException
, std::exception
)
250 const char EVENT_ON_NEW
[] = "OnNew"; // Doc UI event
251 const char EVENT_ON_LOAD
[] = "OnLoad"; // Doc UI event
252 const char EVENT_ON_CREATE
[] = "OnCreate"; // Doc API event
253 const char EVENT_ON_LOAD_FINISHED
[] = "OnLoadFinished"; // Doc API event
254 OUString
EVENT_ON_DOCUMENT_OPENED("onDocumentOpened"); // Job UI event : OnNew or OnLoad
255 OUString
EVENT_ON_DOCUMENT_ADDED("onDocumentAdded"); // Job API event : OnCreate or OnLoadFinished
257 OUString aModuleIdentifier
;
258 ::std::vector
< JobData::TJob2DocEventBinding
> lJobs
;
261 osl::MutexGuard
g(rBHelper
.rMutex
);
264 // Check if the given event name exist inside configuration and reject wrong requests.
265 // This optimization suppress using of the cfg api for getting event and job descriptions.
266 // see using of m_lEvents.find() below ...
268 // retrieve event context from event source
271 aModuleIdentifier
= css::frame::ModuleManager::create( m_xContext
)->identify( aEvent
.Source
);
273 catch( const css::uno::Exception
& )
276 // Special feature: If the events "OnNew" or "OnLoad" occurs - we generate our own event "onDocumentOpened".
278 (aEvent
.EventName
== EVENT_ON_NEW
) ||
279 (aEvent
.EventName
== EVENT_ON_LOAD
)
282 if (find(m_lEvents
, EVENT_ON_DOCUMENT_OPENED
) != m_lEvents
.end())
283 JobData::appendEnabledJobsForEvent(m_xContext
, EVENT_ON_DOCUMENT_OPENED
, lJobs
);
286 // Special feature: If the events "OnCreate" or "OnLoadFinished" occurs - we generate our own event "onDocumentAdded".
288 (aEvent
.EventName
== EVENT_ON_CREATE
) ||
289 (aEvent
.EventName
== EVENT_ON_LOAD_FINISHED
)
292 if (find(m_lEvents
, EVENT_ON_DOCUMENT_ADDED
) != m_lEvents
.end())
293 JobData::appendEnabledJobsForEvent(m_xContext
, EVENT_ON_DOCUMENT_ADDED
, lJobs
);
296 // Add all jobs for "real" notified event too .-)
297 if (find(m_lEvents
, aEvent
.EventName
) != m_lEvents
.end())
298 JobData::appendEnabledJobsForEvent(m_xContext
, aEvent
.EventName
, lJobs
);
301 // step over all enabled jobs and execute it
302 ::std::vector
< JobData::TJob2DocEventBinding
>::const_iterator pIt
;
303 for ( pIt
= lJobs
.begin();
307 rtl::Reference
<Job
> pJob
;
312 const JobData::TJob2DocEventBinding
& rBinding
= *pIt
;
314 JobData
aCfg(m_xContext
);
315 aCfg
.setEvent(rBinding
.m_sDocEvent
, rBinding
.m_sJobName
);
316 aCfg
.setEnvironment(JobData::E_DOCUMENTEVENT
);
318 if (!aCfg
.hasCorrectContext(aModuleIdentifier
))
322 Jobs implements interfaces and dies by ref count!
323 And freeing of such uno object is done by uno itself.
324 So we have to use dynamic memory everytimes.
326 css::uno::Reference
< css::frame::XModel
> xModel(aEvent
.Source
, css::uno::UNO_QUERY
);
327 pJob
= new Job(m_xContext
, xModel
);
328 pJob
->setJobData(aCfg
);
331 pJob
->execute(css::uno::Sequence
< css::beans::NamedValue
>());
335 void SAL_CALL
JobExecutor::elementInserted( const css::container::ContainerEvent
& aEvent
) throw(css::uno::RuntimeException
, std::exception
)
338 if (aEvent
.Accessor
>>= sValue
)
340 OUString sEvent
= ::utl::extractFirstFromConfigurationPath(sValue
);
341 if (!sEvent
.isEmpty())
343 OUStringList::iterator pEvent
= find(m_lEvents
, sEvent
);
344 if (pEvent
== m_lEvents
.end())
345 m_lEvents
.push_back(sEvent
);
350 void SAL_CALL
JobExecutor::elementRemoved ( const css::container::ContainerEvent
& aEvent
) throw(css::uno::RuntimeException
, std::exception
)
353 if (aEvent
.Accessor
>>= sValue
)
355 OUString sEvent
= ::utl::extractFirstFromConfigurationPath(sValue
);
356 if (!sEvent
.isEmpty())
358 OUStringList::iterator pEvent
= find(m_lEvents
, sEvent
);
359 if (pEvent
!= m_lEvents
.end())
360 m_lEvents
.erase(pEvent
);
365 void SAL_CALL
JobExecutor::elementReplaced( const css::container::ContainerEvent
& ) throw(css::uno::RuntimeException
, std::exception
)
367 // I'm not interested on changed items :-)
370 /** @short the used cfg changes notifier wish to be released in its reference.
372 @descr We close our internal used configuration instance to
375 @attention For the special feature "bind global document event broadcaster to job execution"
376 this job executor instance was registered from outside code as
377 css.document.XEventListener. So it can be, that this disposing call comes from
378 the global event broadcaster service. But we don't hold any reference to this service
379 which can or must be released. Because this broadcaster itself is an one instance service
380 too, we can ignore this request. On the other side we must relase our internal CFG
381 reference ... SOLUTION => check the given event source and react only, if it's our internal
382 hold configuration object!
384 void SAL_CALL
JobExecutor::disposing( const css::lang::EventObject
& aEvent
) throw(css::uno::RuntimeException
, std::exception
)
387 osl::MutexGuard
g(rBHelper
.rMutex
);
388 css::uno::Reference
< css::uno::XInterface
> xCFG(m_aConfig
.cfg(), css::uno::UNO_QUERY
);
390 (xCFG
== aEvent
.Source
) &&
391 (m_aConfig
.getMode() != ConfigAccess::E_CLOSED
)
401 css::uno::Reference
<css::uno::XComponentContext
> const & context
):
403 static_cast<cppu::OWeakObject
*>(new JobExecutor(context
)))
405 // 2nd phase initialization needed
406 static_cast<JobExecutor
*>(static_cast<cppu::OWeakObject
*>
407 (instance
.get()))->initListeners();
410 rtl::Reference
<css::uno::XInterface
> instance
;
414 public rtl::StaticWithArg
<
415 Instance
, css::uno::Reference
<css::uno::XComponentContext
>, Singleton
>
420 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
* SAL_CALL
421 com_sun_star_comp_framework_JobExecutor_get_implementation(
422 css::uno::XComponentContext
*context
,
423 css::uno::Sequence
<css::uno::Any
> const &)
425 return cppu::acquire(static_cast<cppu::OWeakObject
*>(
426 Singleton::get(context
).instance
.get()));
429 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */