1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_framework.hxx"
30 
31 //________________________________
32 //	my own includes
33 #include <jobs/jobexecutor.hxx>
34 #include <jobs/job.hxx>
35 #include <jobs/joburl.hxx>
36 
37 #ifndef __FRAMEWORK_CLASS_CONVERTER_HXX_
38 #include <classes/converter.hxx>
39 #endif
40 #include <threadhelp/transactionguard.hxx>
41 #include <threadhelp/readguard.hxx>
42 #include <threadhelp/writeguard.hxx>
43 #include <general.h>
44 #include <services.h>
45 
46 //________________________________
47 //	interface includes
48 #include <com/sun/star/beans/XPropertySet.hpp>
49 #include <com/sun/star/container/XNameAccess.hpp>
50 #include <com/sun/star/container/XContainer.hpp>
51 
52 //________________________________
53 //	includes of other projects
54 #include <unotools/configpathes.hxx>
55 #include <rtl/ustrbuf.hxx>
56 #include <vcl/svapp.hxx>
57 
58 #include <rtl/logfile.hxx>
59 
60 //________________________________
61 //	namespace
62 
63 namespace framework{
64 
65 //________________________________
66 //	non exported const
67 
68 //________________________________
69 //	non exported definitions
70 
71 //________________________________
72 //	declarations
73 
74 DEFINE_XINTERFACE_6( JobExecutor                                                               ,
75                      OWeakObject                                                               ,
76                      DIRECT_INTERFACE(css::lang::XTypeProvider                                ),
77                      DIRECT_INTERFACE(css::lang::XServiceInfo                                 ),
78                      DIRECT_INTERFACE(css::task::XJobExecutor                                 ),
79                      DIRECT_INTERFACE(css::container::XContainerListener                      ),
80                      DIRECT_INTERFACE(css::document::XEventListener                           ),
81                      DERIVED_INTERFACE(css::lang::XEventListener,css::document::XEventListener)
82                    )
83 
84 DEFINE_XTYPEPROVIDER_6( JobExecutor                       ,
85                         css::lang::XTypeProvider          ,
86                         css::lang::XServiceInfo           ,
87                         css::task::XJobExecutor           ,
88                         css::container::XContainerListener,
89                         css::document::XEventListener     ,
90                         css::lang::XEventListener
91                       )
92 
93 DEFINE_XSERVICEINFO_ONEINSTANCESERVICE( JobExecutor                   ,
94                                         ::cppu::OWeakObject           ,
95                                         SERVICENAME_JOBEXECUTOR       ,
96                                         IMPLEMENTATIONNAME_JOBEXECUTOR
97                                       )
98 
99 DEFINE_INIT_SERVICE( JobExecutor,
100                      {
101                          m_xModuleManager = css::uno::Reference< css::frame::XModuleManager >(
102                              m_xSMGR->createInstance(
103                                  SERVICENAME_MODULEMANAGER ),
104                              css::uno::UNO_QUERY_THROW );
105 
106                          /*Attention
107                              I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance()
108                              to create a new instance of this class by our own supported service factory.
109                              see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further informations!
110                          */
111                         // read the list of all currently registered events inside configuration.
112                         // e.g. "/org.openoffice.Office.Jobs/Events/<event name>"
113                         // We need it later to check if an incoming event request can be executed successfully
114                         // or must be rejected. It's an optimization! Of course we must implement updating of this
115                         // list too ... Be listener at the configuration.
116 
117                         m_aConfig.open(ConfigAccess::E_READONLY);
118                         if (m_aConfig.getMode() == ConfigAccess::E_READONLY)
119                         {
120                             css::uno::Reference< css::container::XNameAccess > xRegistry(m_aConfig.cfg(), css::uno::UNO_QUERY);
121                             if (xRegistry.is())
122                                 m_lEvents = Converter::convert_seqOUString2OUStringList(xRegistry->getElementNames());
123 
124                             css::uno::Reference< css::container::XContainer > xNotifier(m_aConfig.cfg(), css::uno::UNO_QUERY);
125                             if (xNotifier.is())
126                             {
127                                 css::uno::Reference< css::container::XContainerListener > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY);
128                                 xNotifier->addContainerListener(xThis);
129                             }
130 
131                             // don't close cfg here!
132                             // It will be done inside disposing ...
133                         }
134                      }
135                    )
136 
137 //________________________________
138 
139 /**
140     @short      standard ctor
141     @descr      It initialize this new instance.
142 
143     @param      xSMGR
144                     reference to the uno service manager
145  */
146 JobExecutor::JobExecutor( /*IN*/ const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR )
147     : ThreadHelpBase      (&Application::GetSolarMutex()                                   )
148     , ::cppu::OWeakObject (                                                                )
149     , m_xSMGR             (xSMGR                                                           )
150     , m_xModuleManager    (                                                                )
151     , m_aConfig           (xSMGR, ::rtl::OUString::createFromAscii(JobData::EVENTCFG_ROOT) )
152 {
153     // Don't do any reference related code here! Do it inside special
154     // impl_ method() ... see DEFINE_INIT_SERVICE() macro for further informations.
155 }
156 
157 JobExecutor::~JobExecutor()
158 {
159     LOG_ASSERT(m_aConfig.getMode() == ConfigAccess::E_CLOSED, "JobExecutor::~JobExecutor()\nConfiguration don't send dispoing() message!\n")
160 }
161 
162 //________________________________
163 
164 /**
165     @short  implementation of XJobExecutor interface
166     @descr  We use the given event to locate any registered job inside our configuration
167             and execute it. Further we control the lifetime of it and supress
168             shutdown of the office till all jobs was finished.
169 
170     @param  sEvent
171                 is used to locate registered jobs
172  */
173 void SAL_CALL JobExecutor::trigger( const ::rtl::OUString& sEvent ) throw(css::uno::RuntimeException)
174 {
175     RTL_LOGFILE_CONTEXT(aLog, "fwk (as96863) JobExecutor::trigger()");
176 
177     /* SAFE { */
178     ReadGuard aReadLock(m_aLock);
179 
180     // Optimization!
181     // Check if the given event name exist inside configuration and reject wrong requests.
182     // This optimization supress using of the cfg api for getting event and job descriptions ...
183     if (m_lEvents.find(sEvent) == m_lEvents.end())
184         return;
185 
186     // get list of all enabled jobs
187     // The called static helper methods read it from the configuration and
188     // filter disabled jobs using it's time stamp values.
189     css::uno::Sequence< ::rtl::OUString > lJobs = JobData::getEnabledJobsForEvent(m_xSMGR, sEvent);
190 
191     aReadLock.unlock();
192     /* } SAFE */
193 
194     // step over all enabled jobs and execute it
195     sal_Int32 c = lJobs.getLength();
196     for (sal_Int32 j=0; j<c; ++j)
197     {
198         /* SAFE { */
199         aReadLock.lock();
200 
201         JobData aCfg(m_xSMGR);
202         aCfg.setEvent(sEvent, lJobs[j]);
203         aCfg.setEnvironment(JobData::E_EXECUTION);
204 
205         /*Attention!
206             Jobs implements interfaces and dies by ref count!
207             And freeing of such uno object is done by uno itself.
208             So we have to use dynamic memory everytimes.
209          */
210         Job* pJob = new Job(m_xSMGR, css::uno::Reference< css::frame::XFrame >());
211         css::uno::Reference< css::uno::XInterface > xJob(static_cast< ::cppu::OWeakObject* >(pJob), css::uno::UNO_QUERY);
212         pJob->setJobData(aCfg);
213 
214         aReadLock.unlock();
215         /* } SAFE */
216 
217         pJob->execute(css::uno::Sequence< css::beans::NamedValue >());
218     }
219 }
220 
221 //________________________________
222 
223 void SAL_CALL JobExecutor::notifyEvent( const css::document::EventObject& aEvent ) throw(css::uno::RuntimeException)
224 {
225     static ::rtl::OUString EVENT_ON_NEW             = DECLARE_ASCII("OnNew"             ); // Doc UI  event
226     static ::rtl::OUString EVENT_ON_LOAD            = DECLARE_ASCII("OnLoad"            ); // Doc UI  event
227     static ::rtl::OUString EVENT_ON_CREATE          = DECLARE_ASCII("OnCreate"          ); // Doc API event
228     static ::rtl::OUString EVENT_ON_LOAD_FINISHED   = DECLARE_ASCII("OnLoadFinished"    ); // Doc API event
229     static ::rtl::OUString EVENT_ON_DOCUMENT_OPENED = DECLARE_ASCII("onDocumentOpened"  ); // Job UI  event : OnNew    or OnLoad
230     static ::rtl::OUString EVENT_ON_DOCUMENT_ADDED  = DECLARE_ASCII("onDocumentAdded"   ); // Job API event : OnCreate or OnLoadFinished
231 
232     /* SAFE { */
233     ReadGuard aReadLock(m_aLock);
234 
235     ::comphelper::SequenceAsVector< JobData::TJob2DocEventBinding > lJobs;
236 
237     // Optimization!
238     // Check if the given event name exist inside configuration and reject wrong requests.
239     // This optimization supress using of the cfg api for getting event and job descriptions.
240     // see using of m_lEvents.find() below ...
241 
242     // retrieve event context from event source
243     rtl::OUString aModuleIdentifier;
244     try
245     {
246         aModuleIdentifier = m_xModuleManager->identify( aEvent.Source );
247     }
248     catch( css::uno::Exception& )
249     {}
250 
251     // Special feature: If the events "OnNew" or "OnLoad" occures - we generate our own event "onDocumentOpened".
252     if (
253         (aEvent.EventName.equals(EVENT_ON_NEW )) ||
254         (aEvent.EventName.equals(EVENT_ON_LOAD))
255        )
256     {
257         if (m_lEvents.find(EVENT_ON_DOCUMENT_OPENED) != m_lEvents.end())
258             JobData::appendEnabledJobsForEvent(m_xSMGR, EVENT_ON_DOCUMENT_OPENED, lJobs);
259     }
260 
261     // Special feature: If the events "OnCreate" or "OnLoadFinished" occures - we generate our own event "onDocumentAdded".
262     if (
263         (aEvent.EventName.equals(EVENT_ON_CREATE       )) ||
264         (aEvent.EventName.equals(EVENT_ON_LOAD_FINISHED))
265        )
266     {
267         if (m_lEvents.find(EVENT_ON_DOCUMENT_ADDED) != m_lEvents.end())
268             JobData::appendEnabledJobsForEvent(m_xSMGR, EVENT_ON_DOCUMENT_ADDED, lJobs);
269     }
270 
271     // Add all jobs for "real" notified event too .-)
272     if (m_lEvents.find(aEvent.EventName) != m_lEvents.end())
273         JobData::appendEnabledJobsForEvent(m_xSMGR, aEvent.EventName, lJobs);
274 
275     aReadLock.unlock();
276     /* } SAFE */
277 
278     // step over all enabled jobs and execute it
279     ::comphelper::SequenceAsVector< JobData::TJob2DocEventBinding >::const_iterator pIt;
280     for (  pIt  = lJobs.begin();
281            pIt != lJobs.end()  ;
282          ++pIt                 )
283     {
284         /* SAFE { */
285         aReadLock.lock();
286 
287         const JobData::TJob2DocEventBinding& rBinding = *pIt;
288 
289         JobData aCfg(m_xSMGR);
290         aCfg.setEvent(rBinding.m_sDocEvent, rBinding.m_sJobName);
291         aCfg.setEnvironment(JobData::E_DOCUMENTEVENT);
292 
293         if (!aCfg.hasCorrectContext(aModuleIdentifier))
294             continue;
295 
296         /*Attention!
297             Jobs implements interfaces and dies by ref count!
298             And freeing of such uno object is done by uno itself.
299             So we have to use dynamic memory everytimes.
300          */
301         css::uno::Reference< css::frame::XModel > xModel(aEvent.Source, css::uno::UNO_QUERY);
302         Job* pJob = new Job(m_xSMGR, xModel);
303         css::uno::Reference< css::uno::XInterface > xJob(static_cast< ::cppu::OWeakObject* >(pJob), css::uno::UNO_QUERY);
304         pJob->setJobData(aCfg);
305 
306         aReadLock.unlock();
307         /* } SAFE */
308 
309         pJob->execute(css::uno::Sequence< css::beans::NamedValue >());
310     }
311 }
312 
313 //________________________________
314 
315 void SAL_CALL JobExecutor::elementInserted( const css::container::ContainerEvent& aEvent ) throw(css::uno::RuntimeException)
316 {
317     ::rtl::OUString sValue;
318     if (aEvent.Accessor >>= sValue)
319     {
320         ::rtl::OUString sEvent = ::utl::extractFirstFromConfigurationPath(sValue);
321         if (sEvent.getLength() > 0)
322         {
323             OUStringList::iterator pEvent = m_lEvents.find(sEvent);
324             if (pEvent == m_lEvents.end())
325                 m_lEvents.push_back(sEvent);
326         }
327     }
328 }
329 
330 void SAL_CALL JobExecutor::elementRemoved ( const css::container::ContainerEvent& aEvent ) throw(css::uno::RuntimeException)
331 {
332     ::rtl::OUString sValue;
333     if (aEvent.Accessor >>= sValue)
334     {
335         ::rtl::OUString sEvent = ::utl::extractFirstFromConfigurationPath(sValue);
336         if (sEvent.getLength() > 0)
337         {
338             OUStringList::iterator pEvent = m_lEvents.find(sEvent);
339             if (pEvent != m_lEvents.end())
340                 m_lEvents.erase(pEvent);
341         }
342     }
343 }
344 
345 void SAL_CALL JobExecutor::elementReplaced( const css::container::ContainerEvent& ) throw(css::uno::RuntimeException)
346 {
347     // I'm not interested on changed items :-)
348 }
349 
350 //________________________________
351 
352 /** @short  the used cfg changes notifier wish to be released in its reference.
353 
354     @descr  We close our internal used configuration instance to
355             free this reference.
356 
357     @attention  For the special feature "bind global document event broadcaster to job execution"
358                 this job executor instance was registered from outside code as
359                 css.document.XEventListener. So it can be, that this disposing call comes from
360                 the global event broadcaster service. But we don't hold any reference to this service
361                 which can or must be released. Because this broadcaster itself is an one instance service
362                 too, we can ignore this request. On the other side we must relase our internal CFG
363                 reference ... SOLUTION => check the given event source and react only, if it's our internal
364                 hold configuration object!
365  */
366 void SAL_CALL JobExecutor::disposing( const css::lang::EventObject& aEvent ) throw(css::uno::RuntimeException)
367 {
368     /* SAFE { */
369     ReadGuard aReadLock(m_aLock);
370     css::uno::Reference< css::uno::XInterface > xCFG(m_aConfig.cfg(), css::uno::UNO_QUERY);
371     if (
372         (xCFG                == aEvent.Source        ) &&
373         (m_aConfig.getMode() != ConfigAccess::E_CLOSED)
374        )
375     {
376         m_aConfig.close();
377     }
378     aReadLock.unlock();
379     /* } SAFE */
380 }
381 
382 } // namespace framework
383