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