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 
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 
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 supress
164             shutdown of the office till all jobs was finished.
165 
166     @param  sEvent
167                 is used to locate registered jobs
168  */
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 supress 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 
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 supress 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" occures - 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" occures - 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 
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 
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 
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  */
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