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
DEFINE_XINTERFACE_6(JobExecutor,OWeakObject,DIRECT_INTERFACE (css::lang::XTypeProvider),DIRECT_INTERFACE (css::lang::XServiceInfo),DIRECT_INTERFACE (css::task::XJobExecutor),DIRECT_INTERFACE (css::container::XContainerListener),DIRECT_INTERFACE (css::document::XEventListener),DERIVED_INTERFACE (css::lang::XEventListener,css::document::XEventListener))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
~JobExecutor()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 suppress
164 shutdown of the office till all jobs was finished.
165
166 @param sEvent
167 is used to locate registered jobs
168 */
trigger(const::rtl::OUString & sEvent)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 suppress 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
notifyEvent(const css::document::EventObject & aEvent)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 suppress 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" occurs - 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" occurs - 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
elementInserted(const css::container::ContainerEvent & aEvent)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
elementRemoved(const css::container::ContainerEvent & aEvent)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
elementReplaced(const css::container::ContainerEvent &)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 */
disposing(const css::lang::EventObject & aEvent)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