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/jobdispatch.hxx>
34 #include <jobs/joburl.hxx>
35 #include <jobs/job.hxx>
36 #include <threadhelp/readguard.hxx>
37 #include <threadhelp/writeguard.hxx>
38 #include <threadhelp/resetableguard.hxx>
39 #include <classes/converter.hxx>
40 #include <general.h>
41 #include <services.h>
42 
43 //________________________________
44 //	interface includes
45 #include <com/sun/star/beans/XPropertySet.hpp>
46 #include <com/sun/star/frame/DispatchResultState.hpp>
47 #include <com/sun/star/frame/XModuleManager.hpp>
48 
49 //________________________________
50 //	includes of other projects
51 #include <rtl/ustrbuf.hxx>
52 #include <vcl/svapp.hxx>
53 
54 //________________________________
55 //	namespace
56 
57 namespace framework{
58 
59 //________________________________
60 //	non exported const
61 
62 //________________________________
63 //	non exported definitions
64 
65 //________________________________
66 //	declarations
67 
68 DEFINE_XINTERFACE_6( JobDispatch                                     ,
69                      OWeakObject                                     ,
70                      DIRECT_INTERFACE(css::lang::XTypeProvider      ),
71                      DIRECT_INTERFACE(css::frame::XDispatchProvider ),
72                      DIRECT_INTERFACE(css::lang::XInitialization ),
73                      DIRECT_INTERFACE(css::lang::XServiceInfo),
74                      DIRECT_INTERFACE(css::frame::XNotifyingDispatch),
75                      DIRECT_INTERFACE(css::frame::XDispatch         )
76                    )
77 
78 DEFINE_XTYPEPROVIDER_6( JobDispatch                   ,
79                         css::lang::XTypeProvider      ,
80                         css::frame::XDispatchProvider ,
81                         css::frame::XNotifyingDispatch,
82 						css::lang::XInitialization,
83 						css::lang::XServiceInfo,
84                         css::frame::XDispatch
85                       )
86 
87 DEFINE_XSERVICEINFO_MULTISERVICE( JobDispatch                   ,
88                                   ::cppu::OWeakObject           ,
89                                   SERVICENAME_PROTOCOLHANDLER   ,
90                                   IMPLEMENTATIONNAME_JOBDISPATCH
91                                 )
92 
93 DEFINE_INIT_SERVICE( JobDispatch,
94                      {
95                          /*Attention
96                              I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance()
97                              to create a new instance of this class by our own supported service factory.
98                              see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further informations!
99                          */
100                      }
101                    )
102 
103 //________________________________
104 /**
105     @short      standard ctor
106     @descr      It initialize this new instance.
107 
108     @param      xSMGR
109                     reference to the uno service manager
110 */
111 JobDispatch::JobDispatch( /*IN*/ const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR )
112     : ThreadHelpBase(&Application::GetSolarMutex())
113     , OWeakObject   (                             )
114     , m_xSMGR       (xSMGR                        )
115 {
116 }
117 
118 //________________________________
119 /**
120     @short  let this instance die
121     @descr  We have to release all used ressources and free used memory.
122 */
123 JobDispatch::~JobDispatch()
124 {
125     // release all used ressources
126     m_xSMGR  = css::uno::Reference< css::lang::XMultiServiceFactory >();
127     m_xFrame = css::uno::Reference< css::frame::XFrame >();
128 }
129 
130 //________________________________
131 /**
132     @short  implementation of XInitalization
133     @descr  A protocol handler can provide this functionality, if it wish to get additional informations
134             about the context it runs. In this case the frame reference would be given by the outside code.
135 
136     @param  lArguments
137                 the list of initialization arguments
138                 First parameter should be the frame reference we need.
139 */
140 void SAL_CALL JobDispatch::initialize( const css::uno::Sequence< css::uno::Any >& lArguments ) throw(css::uno::Exception       ,
141                                                                                                      css::uno::RuntimeException)
142 {
143     /* SAFE { */
144     WriteGuard aWriteLock(m_aLock);
145 
146     for (int a=0; a<lArguments.getLength(); ++a)
147     {
148         if (a==0)
149         {
150             lArguments[a] >>= m_xFrame;
151 
152             css::uno::Reference< css::frame::XModuleManager > xModuleManager(
153                 m_xSMGR->createInstance(
154                     SERVICENAME_MODULEMANAGER ),
155                 css::uno::UNO_QUERY_THROW );
156             try
157             {
158                 m_sModuleIdentifier = xModuleManager->identify( m_xFrame );
159             }
160             catch( css::uno::Exception& )
161             {}
162         }
163     }
164 
165     aWriteLock.unlock();
166     /* } SAFE */
167 }
168 
169 //________________________________
170 /**
171     @short  implementation of XDispatchProvider::queryDispatches()
172     @descr  Every protocol handler will be asked for his agreement, if an URL was queried
173             for which this handler is registered. It's the chance for this handler to validate
174             the given URL and return a dispatch object (may be itself) or not.
175 
176     @param  aURL
177                 the queried URL, which should be checked
178 
179     @param  sTargetFrameName
180                 describes the target frame, in which context this handler will be used
181                 Is mostly set to "", "_self", "_blank", "_default" or a non special one
182                 using SELF/CREATE as search flags.
183 
184     @param  nSearchFlags
185                 Can be SELF or CREATE only and are set only if sTargetFrameName isn't a special target
186 */
187 css::uno::Reference< css::frame::XDispatch > SAL_CALL JobDispatch::queryDispatch( /*IN*/ const css::util::URL&  aURL             ,
188                                                                                   /*IN*/ const ::rtl::OUString& /*sTargetFrameName*/ ,
189                                                                                   /*IN*/       sal_Int32        /*nSearchFlags*/     ) throw(css::uno::RuntimeException)
190 {
191     css::uno::Reference< css::frame::XDispatch > xDispatch;
192 
193     JobURL aAnalyzedURL(aURL.Complete);
194     if (aAnalyzedURL.isValid())
195         xDispatch = css::uno::Reference< css::frame::XDispatch >( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY );
196 
197     return xDispatch;
198 }
199 
200 //________________________________
201 /**
202     @short  implementation of XDispatchProvider::queryDispatches()
203     @descr  It's an optimized access for remote, so you can ask for
204             multiple dispatch objects at the same time.
205 
206     @param  lDescriptor
207                 a list of queryDispatch() parameter
208 
209     @return A list of corresponding dispatch objects.
210             NULL references are not skipped. Every result
211             match to one given descriptor item.
212 */
213 css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL JobDispatch::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) throw(css::uno::RuntimeException)
214 {
215     // don't pack resulting list!
216     sal_Int32 nCount = lDescriptor.getLength();
217     css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > lDispatches(nCount);
218 
219     for (sal_Int32 i=0; i<nCount; ++i)
220         lDispatches[i] = queryDispatch( lDescriptor[i].FeatureURL  ,
221                                         lDescriptor[i].FrameName   ,
222                                         lDescriptor[i].SearchFlags );
223     return lDispatches;
224 }
225 
226 //________________________________
227 /**
228     @short  implementation of XNotifyingDispatch::dispatchWithNotification()
229     @descr  It creates the job service implementation and call execute on it.
230             Further it starts the life time control of it. (important for async job)
231             For synchonrous job we react for the returned result directly ... for asynchronous
232             ones we do it later inside our callback method. But we use the same impl method
233             doing that to share the code. (see impl_finishJob())
234 
235             If a job is already running, (it can only occure for asynchronous jobs)
236             don't start the same job a second time. Queue in the given dispatch parameter
237             and return immediatly. If the current running job call us back, we will start this
238             new dispatch request.
239             If no job is running - queue the parameter too! But then start the new job immediatly.
240             We have to queue it every time - because it hold us alive by ref count!
241 
242     @param  aURL
243                 describe the job(s), which should be started
244 
245     @param  lArgs
246                 optional arguments for this request
247 
248     @param  xListener
249                 an interested listener for possible results of this operation
250 */
251 void SAL_CALL JobDispatch::dispatchWithNotification( /*IN*/ const css::util::URL&                                             aURL      ,
252                                                      /*IN*/ const css::uno::Sequence< css::beans::PropertyValue >&            lArgs     ,
253                                                      /*IN*/ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) throw(css::uno::RuntimeException)
254 {
255     JobURL aAnalyzedURL(aURL.Complete);
256     if (aAnalyzedURL.isValid())
257     {
258         ::rtl::OUString sRequest;
259         if (aAnalyzedURL.getEvent(sRequest))
260             impl_dispatchEvent(sRequest, lArgs, xListener);
261         else
262         if (aAnalyzedURL.getService(sRequest))
263             impl_dispatchService(sRequest, lArgs, xListener);
264         else
265         if (aAnalyzedURL.getAlias(sRequest))
266             impl_dispatchAlias(sRequest, lArgs, xListener);
267     }
268 }
269 
270 //________________________________
271 /**
272     @short  dispatch an event
273     @descr  We search all registered jobs for this event and execute it.
274             After doing so, we inform the given listener about the results.
275             (There will be one notify for every executed job!)
276 
277     @param  sEvent
278                 the event, for which jobs can be registered
279 
280     @param  lArgs
281                 optional arguments for this request
282                 Currently not used!
283 
284     @param  xListener
285                 an interested listener for possible results of this operation
286 */
287 void JobDispatch::impl_dispatchEvent( /*IN*/ const ::rtl::OUString&                                            sEvent    ,
288                                       /*IN*/ const css::uno::Sequence< css::beans::PropertyValue >&            lArgs     ,
289                                       /*IN*/ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
290 {
291     // get list of all enabled jobs
292     // The called static helper methods read it from the configuration and
293     // filter disabled jobs using it's time stamp values.
294     /* SAFE { */
295     ReadGuard aReadLock(m_aLock);
296     css::uno::Sequence< ::rtl::OUString > lJobs = JobData::getEnabledJobsForEvent(m_xSMGR, sEvent);
297     aReadLock.unlock();
298     /* } SAFE */
299 
300     css::uno::Reference< css::frame::XDispatchResultListener > xThis( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY );
301 
302     // no jobs ... no execution
303     // But a may given listener will know something ...
304     // I think this operaton was finished successfully.
305     // It's not realy an error, if no registered jobs could be located.
306     // Step over all found jobs and execute it
307     int nExecutedJobs=0;
308     for (int j=0; j<lJobs.getLength(); ++j)
309     {
310         /* SAFE { */
311         aReadLock.lock();
312 
313         JobData aCfg(m_xSMGR);
314         aCfg.setEvent(sEvent, lJobs[j]);
315         aCfg.setEnvironment(JobData::E_DISPATCH);
316         const bool bIsEnabled=aCfg.hasCorrectContext(m_sModuleIdentifier);
317 
318         /*Attention!
319             Jobs implements interfaces and dies by ref count!
320             And freeing of such uno object is done by uno itself.
321             So we have to use dynamic memory everytimes.
322          */
323         Job* pJob = new Job(m_xSMGR, m_xFrame);
324         css::uno::Reference< css::uno::XInterface > xJob(static_cast< ::cppu::OWeakObject* >(pJob), css::uno::UNO_QUERY);
325         pJob->setJobData(aCfg);
326 
327         aReadLock.unlock();
328         /* } SAFE */
329 
330         if (!bIsEnabled)
331             continue;
332 
333         // Special mode for listener.
334         // We dont notify it directly here. We delegate that
335         // to the job implementation. But we must set ourself there too.
336         // Because this job must fake the source adress of the event.
337         // Otherwhise the listener may will ignore it.
338         if (xListener.is())
339             pJob->setDispatchResultFake(xListener, xThis);
340         pJob->execute(Converter::convert_seqPropVal2seqNamedVal(lArgs));
341         ++nExecutedJobs;
342     }
343 
344     if (nExecutedJobs<1 && xListener.is())
345     {
346         css::frame::DispatchResultEvent aEvent;
347         aEvent.Source = xThis;
348         aEvent.State  = css::frame::DispatchResultState::SUCCESS;
349         xListener->dispatchFinished(aEvent);
350     }
351 }
352 
353 //________________________________
354 /**
355     @short  dispatch a service
356     @descr  We use the given name only to create and if possible to initialize
357             it as an uno service. It can be usefully for creating (caching?)
358             of e.g. one instance services.
359 
360     @param  sService
361                 the uno implementation or service name of the job, which should be instanciated
362 
363     @param  lArgs
364                 optional arguments for this request
365                 Currently not used!
366 
367     @param  xListener
368                 an interested listener for possible results of this operation
369 */
370 void JobDispatch::impl_dispatchService( /*IN*/ const ::rtl::OUString&                                            sService  ,
371                                         /*IN*/ const css::uno::Sequence< css::beans::PropertyValue >&            lArgs     ,
372                                         /*IN*/ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
373 {
374     /* SAFE { */
375     ReadGuard aReadLock(m_aLock);
376 
377     JobData aCfg(m_xSMGR);
378     aCfg.setService(sService);
379     aCfg.setEnvironment(JobData::E_DISPATCH);
380 
381     /*Attention!
382         Jobs implements interfaces and dies by ref count!
383         And freeing of such uno object is done by uno itself.
384         So we have to use dynamic memory everytimes.
385      */
386     Job* pJob = new Job(m_xSMGR, m_xFrame);
387     css::uno::Reference< css::uno::XInterface > xJob(static_cast< ::cppu::OWeakObject* >(pJob), css::uno::UNO_QUERY);
388     pJob->setJobData(aCfg);
389 
390     aReadLock.unlock();
391     /* } SAFE */
392 
393     css::uno::Reference< css::frame::XDispatchResultListener > xThis( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY );
394 
395     // Special mode for listener.
396     // We dont notify it directly here. We delegate that
397     // to the job implementation. But we must set ourself there too.
398     // Because this job must fake the source adress of the event.
399     // Otherwhise the listener may will ignore it.
400     if (xListener.is())
401         pJob->setDispatchResultFake(xListener, xThis);
402     pJob->execute(Converter::convert_seqPropVal2seqNamedVal(lArgs));
403 }
404 
405 //________________________________
406 /**
407     @short  dispatch an alias
408     @descr  We use this alias to locate a job inside the configuration
409             and execute it. Further we inform the given listener about the results.
410 
411     @param  sAlias
412                 the alias name of the configured job
413 
414     @param  lArgs
415                 optional arguments for this request
416                 Currently not used!
417 
418     @param  xListener
419                 an interested listener for possible results of this operation
420 */
421 void JobDispatch::impl_dispatchAlias( /*IN*/ const ::rtl::OUString&                                            sAlias    ,
422                                       /*IN*/ const css::uno::Sequence< css::beans::PropertyValue >&            lArgs     ,
423                                       /*IN*/ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
424 {
425     /* SAFE { */
426     ReadGuard aReadLock(m_aLock);
427 
428     JobData aCfg(m_xSMGR);
429     aCfg.setAlias(sAlias);
430     aCfg.setEnvironment(JobData::E_DISPATCH);
431 
432     /*Attention!
433         Jobs implements interfaces and dies by ref count!
434         And freeing of such uno object is done by uno itself.
435         So we have to use dynamic memory everytimes.
436      */
437     Job* pJob = new Job(m_xSMGR, m_xFrame);
438     css::uno::Reference< css::uno::XInterface > xJob(static_cast< ::cppu::OWeakObject* >(pJob), css::uno::UNO_QUERY);
439     pJob->setJobData(aCfg);
440 
441     aReadLock.unlock();
442     /* } SAFE */
443 
444     css::uno::Reference< css::frame::XDispatchResultListener > xThis( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY );
445 
446     // Special mode for listener.
447     // We dont notify it directly here. We delegate that
448     // to the job implementation. But we must set ourself there too.
449     // Because this job must fake the source adress of the event.
450     // Otherwhise the listener may will ignore it.
451     if (xListener.is())
452         pJob->setDispatchResultFake(xListener, xThis);
453     pJob->execute(Converter::convert_seqPropVal2seqNamedVal(lArgs));
454 }
455 
456 //________________________________
457 /**
458     @short  implementation of XDispatch::dispatch()
459     @descr  Because the methods dispatch() and dispatchWithNotification() are different in her parameters
460             only, we can forward this request to dispatchWithNotification() by using an empty listener!
461 
462     @param  aURL
463                 describe the job(s), which should be started
464 
465     @param  lArgs
466                 optional arguments for this request
467 
468     @see    dispatchWithNotification()
469 */
470 void SAL_CALL JobDispatch::dispatch( /*IN*/ const css::util::URL&                                  aURL  ,
471                                      /*IN*/ const css::uno::Sequence< css::beans::PropertyValue >& lArgs ) throw(css::uno::RuntimeException)
472 {
473     dispatchWithNotification(aURL, lArgs, css::uno::Reference< css::frame::XDispatchResultListener >());
474 }
475 
476 //________________________________
477 /**
478     @short  not supported
479 */
480 void SAL_CALL JobDispatch::addStatusListener( /*IN*/ const css::uno::Reference< css::frame::XStatusListener >&,
481                                               /*IN*/ const css::util::URL&                                      ) throw(css::uno::RuntimeException)
482 {
483 }
484 
485 //________________________________
486 /**
487     @short  not supported
488 */
489 void SAL_CALL JobDispatch::removeStatusListener( /*IN*/ const css::uno::Reference< css::frame::XStatusListener >&,
490                                                  /*IN*/ const css::util::URL&                                          ) throw(css::uno::RuntimeException)
491 {
492 }
493 
494 } // namespace framework
495