xref: /aoo42x/main/framework/source/jobs/job.cxx (revision 6d739b60)
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/job.hxx>
30 #include <threadhelp/readguard.hxx>
31 #include <threadhelp/writeguard.hxx>
32 #include <general.h>
33 #include <services.h>
34 
35 //________________________________
36 //	interface includes
37 #include <com/sun/star/task/XJob.hpp>
38 #include <com/sun/star/task/XAsyncJob.hpp>
39 #include <com/sun/star/util/XCloseBroadcaster.hpp>
40 #include <com/sun/star/util/XCloseable.hpp>
41 #include <com/sun/star/lang/DisposedException.hpp>
42 
43 //________________________________
44 //	includes of other projects
45 #include <rtl/ustrbuf.hxx>
46 #include <vcl/svapp.hxx>
47 
48 //________________________________
49 //	namespace
50 
51 namespace framework{
52 
53 //________________________________
54 //	non exported const
55 
56 //________________________________
57 //	non exported definitions
58 
59 //________________________________
60 //	declarations
61 
62 DEFINE_XINTERFACE_4( Job                                             ,
63                      OWeakObject                                     ,
64                      DIRECT_INTERFACE(css::lang::XTypeProvider      ),
65                      DIRECT_INTERFACE(css::task::XJobListener       ),
66                      DIRECT_INTERFACE(css::frame::XTerminateListener),
67                      DIRECT_INTERFACE(css::util::XCloseListener     )
68                    )
69 
70 DEFINE_XTYPEPROVIDER_4( Job                           ,
71                         css::lang::XTypeProvider      ,
72                         css::task::XJobListener       ,
73                         css::frame::XTerminateListener,
74                         css::util::XCloseListener
75                       )
76 
77 //________________________________
78 /**
79     @short      standard ctor
80     @descr      It initialize this new instance. But it set some generic parameters here only.
81                 Specialized informations (e.g. the alias or service name ofthis job) will be set
82                 later using the method setJobData().
83 
84     @param      xSMGR
85                 reference to the uno service manager
86 
87     @param      xFrame
88                 reference to the frame, in which environment we run
89                 (May be null!)
90 */
91 Job::Job( /*IN*/ const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR  ,
92           /*IN*/ const css::uno::Reference< css::frame::XFrame >&              xFrame )
93     : ThreadHelpBase       (&Application::GetSolarMutex())
94     , ::cppu::OWeakObject  (                             )
95     , m_aJobCfg            (xSMGR                        )
96     , m_xSMGR              (xSMGR                        )
97     , m_xFrame             (xFrame                       )
98     , m_bListenOnDesktop   (sal_False                    )
99     , m_bListenOnFrame     (sal_False                    )
100     , m_bListenOnModel     (sal_False                    )
101     , m_bPendingCloseFrame (sal_False                    )
102     , m_bPendingCloseModel (sal_False                    )
103     , m_eRunState          (E_NEW                        )
104 {
105 }
106 
107 //________________________________
108 /**
109     @short      standard ctor
110     @descr      It initialize this new instance. But it set some generic parameters here only.
111                 Specialized informations (e.g. the alias or service name ofthis job) will be set
112                 later using the method setJobData().
113 
114     @param      xSMGR
115                 reference to the uno service manager
116 
117     @param      xModel
118                 reference to the model, in which environment we run
119                 (May be null!)
120 */
121 Job::Job( /*IN*/ const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR  ,
122           /*IN*/ const css::uno::Reference< css::frame::XModel >&              xModel )
123     : ThreadHelpBase       (&Application::GetSolarMutex())
124     , ::cppu::OWeakObject  (                             )
125     , m_aJobCfg            (xSMGR                        )
126     , m_xSMGR              (xSMGR                        )
127     , m_xModel             (xModel                       )
128     , m_bListenOnDesktop   (sal_False                    )
129     , m_bListenOnFrame     (sal_False                    )
130     , m_bListenOnModel     (sal_False                    )
131     , m_bPendingCloseFrame (sal_False                    )
132     , m_bPendingCloseModel (sal_False                    )
133     , m_eRunState          (E_NEW                        )
134 {
135 }
136 
137 //________________________________
138 /**
139     @short  superflous!
140     @descr  Releasing of memory and reference must be done inside die() call.
141             Otherwhise it's a bug.
142 */
143 Job::~Job()
144 {
145 }
146 
147 //________________________________
148 /**
149     @short  set (or delete) a listener for sending dispatch result events
150     @descr  Because this object is used in a wrapped mode ... the original listener
151             for such events can't be registered here directly. Because the
152             listener expect to get the original object given as source of the event.
153             That's why we get this source here too, to fake(!) it at sending time!
154 
155     @param  xListener
156                 the original listener for dispatch result events
157 
158     @param  xSourceFake
159                 our user, which got the registration request for this listener
160 */
161 void Job::setDispatchResultFake( /*IN*/ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener   ,
162                                  /*IN*/ const css::uno::Reference< css::uno::XInterface >&                xSourceFake )
163 {
164     /* SAFE { */
165     WriteGuard aWriteLock(m_aLock);
166 
167     // reject dangerous calls
168     if (m_eRunState != E_NEW)
169     {
170         LOG_WARNING("Job::setJobData()", "job may still running or already finished")
171         return;
172     }
173 
174     m_xResultListener   = xListener  ;
175     m_xResultSourceFake = xSourceFake;
176     aWriteLock.unlock();
177     /* } SAFE */
178 }
179 
180 void Job::setJobData( const JobData& aData )
181 {
182     /* SAFE { */
183     WriteGuard aWriteLock(m_aLock);
184 
185     // reject dangerous calls
186     if (m_eRunState != E_NEW)
187     {
188         LOG_WARNING("Job::setJobData()", "job may still running or already finished")
189         return;
190     }
191 
192     m_aJobCfg = aData;
193     aWriteLock.unlock();
194     /* } SAFE */
195 }
196 
197 //________________________________
198 /**
199     @short  runs the job
200     @descr  It doesn't matter, if the job is an asynchronous or
201             synchronous one. This method returns only if it was finished
202             or cancelled.
203 
204     @param  lDynamicArgs
205                 optional arguments for job execution
206                 In case the represented job is a configured one (which uses static
207                 arguments too) all informations will be merged!
208 */
209 void Job::execute( /*IN*/ const css::uno::Sequence< css::beans::NamedValue >& lDynamicArgs )
210 {
211     /* SAFE { */
212     WriteGuard aWriteLock(m_aLock);
213 
214     // reject dangerous calls
215     if (m_eRunState != E_NEW)
216     {
217         LOG_WARNING("Job::execute()", "job may still running or already finished")
218         return;
219     }
220 
221     // create the environment and mark this job as running ...
222     m_eRunState = E_RUNNING;
223     impl_startListening();
224 
225     css::uno::Reference< css::task::XAsyncJob >  xAJob;
226     css::uno::Reference< css::task::XJob >       xSJob;
227     css::uno::Sequence< css::beans::NamedValue > lJobArgs = impl_generateJobArgs(lDynamicArgs);
228 
229     // It's neccessary to hold us self alive!
230     // Otherwhise we might die by ref count ...
231     css::uno::Reference< css::task::XJobListener > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY);
232 
233     try
234     {
235         // create the job
236         // We must check for the supported interface on demand!
237         // But we preferr the synchronous one ...
238         m_xJob = m_xSMGR->createInstance(m_aJobCfg.getService());
239         xSJob  = css::uno::Reference< css::task::XJob >(m_xJob, css::uno::UNO_QUERY);
240         if (!xSJob.is())
241             xAJob = css::uno::Reference< css::task::XAsyncJob >(m_xJob, css::uno::UNO_QUERY);
242 
243         // execute it asynchron
244         if (xAJob.is())
245         {
246             m_aAsyncWait.reset();
247             aWriteLock.unlock();
248             /* } SAFE */
249             xAJob->executeAsync(lJobArgs, xThis);
250             // wait for finishing this job - so this method
251             // does the same for synchronous and asynchronous jobs!
252             m_aAsyncWait.wait();
253             aWriteLock.lock();
254             /* SAFE { */
255             // Note: Result handling was already done inside the callback!
256         }
257         // execute it synchron
258         else if (xSJob.is())
259         {
260             aWriteLock.unlock();
261             /* } SAFE */
262             css::uno::Any aResult = xSJob->execute(lJobArgs);
263             aWriteLock.lock();
264             /* SAFE { */
265             impl_reactForJobResult(aResult);
266         }
267     }
268     #if OSL_DEBUG_LEVEL > 0
269     catch(const css::uno::Exception& ex)
270     {
271         ::rtl::OUStringBuffer sMsg(256);
272         sMsg.appendAscii("Got exception during job execution. Original Message was:\n\"");
273         sMsg.append     (ex.Message);
274         sMsg.appendAscii("\"");
275         LOG_WARNING("Job::execute()", U2B(sMsg.makeStringAndClear()).getStr())
276     }
277     #else
278     catch(const css::uno::Exception&)
279         {}
280     #endif
281 
282     // deinitialize the environment and mark this job as finished ...
283     // but don't overwrite any informations about STOPPED or might DISPOSED jobs!
284     impl_stopListening();
285     if (m_eRunState == E_RUNNING)
286         m_eRunState = E_STOPPED_OR_FINISHED;
287 
288     // If we got a close request from our frame or model ...
289     // but we disagreed wit that by throwing a veto exception...
290     // and got the ownership ...
291     // we have to close the resource frame or model now -
292     // and to disable ourself!
293     if (m_bPendingCloseFrame)
294     {
295         m_bPendingCloseFrame = sal_False;
296         css::uno::Reference< css::util::XCloseable > xClose(m_xFrame, css::uno::UNO_QUERY);
297         if (xClose.is())
298         {
299             try
300             {
301                 xClose->close(sal_True);
302             }
303             catch(const css::util::CloseVetoException&) {}
304         }
305     }
306 
307     if (m_bPendingCloseModel)
308     {
309         m_bPendingCloseModel = sal_False;
310         css::uno::Reference< css::util::XCloseable > xClose(m_xModel, css::uno::UNO_QUERY);
311         if (xClose.is())
312         {
313             try
314             {
315                 xClose->close(sal_True);
316             }
317             catch(const css::util::CloseVetoException&) {}
318         }
319     }
320 
321     aWriteLock.unlock();
322     /* SAFE { */
323 
324     // release this instance ...
325     die();
326 }
327 
328 //________________________________
329 /**
330     @short  kill this job
331     @descr  It doesn't matter if this request is called from inside or
332             from outside. We release our internal structures and stop
333             avary activity. After doing so - this instance will not be
334             useable any longer! Of course we try to handle further requests
335             carefully. May somehwere else hold a reference to us ...
336 */
337 void Job::die()
338 {
339     /* SAFE { */
340     WriteGuard aWriteLock(m_aLock);
341 
342     impl_stopListening();
343 
344     if (m_eRunState != E_DISPOSED)
345     {
346         try
347         {
348             css::uno::Reference< css::lang::XComponent > xDispose(m_xJob, css::uno::UNO_QUERY);
349             if (xDispose.is())
350             {
351                 xDispose->dispose();
352                 m_eRunState = E_DISPOSED;
353             }
354         }
355         catch(const css::lang::DisposedException&)
356         {
357             m_eRunState = E_DISPOSED;
358         }
359     }
360 
361     m_xJob               = css::uno::Reference< css::uno::XInterface >();
362     m_xFrame             = css::uno::Reference< css::frame::XFrame >();
363     m_xModel             = css::uno::Reference< css::frame::XModel >();
364     m_xDesktop           = css::uno::Reference< css::frame::XDesktop >();
365     m_xResultListener    = css::uno::Reference< css::frame::XDispatchResultListener >();
366     m_xResultSourceFake  = css::uno::Reference< css::uno::XInterface >();
367     m_bPendingCloseFrame = sal_False;
368     m_bPendingCloseModel = sal_False;
369 
370     aWriteLock.unlock();
371     /* SAFE { */
372 }
373 
374 //________________________________
375 /**
376     @short  generates list of arguments for job execute
377     @descr  There exist a set of informations, which can be needed by a job.
378                 a) it's static configuration data   (Equals for all jobs.    )
379                 b) it's specific configuration data (Different for every job.)
380                 c) some environment values          (e.g. the frame, for which this job was started)
381                 d) any other dynamic data           (e.g. parameters of a dispatch() request)
382             We collect all these informations and generate one list which include all others.
383 
384     @param  lDynamicArgs
385                 list of dynamic arguments (given by a corresponding dispatch() call)
386                 Can be empty too.
387 
388     @return A list which includes all mentioned sub lists.
389 */
390 css::uno::Sequence< css::beans::NamedValue > Job::impl_generateJobArgs( /*IN*/ const css::uno::Sequence< css::beans::NamedValue >& lDynamicArgs )
391 {
392     css::uno::Sequence< css::beans::NamedValue > lAllArgs;
393 
394     /* SAFE { */
395     ReadGuard aReadLock(m_aLock);
396 
397     // the real structure of the returned list depends from the environment of this job!
398     JobData::EMode eMode = m_aJobCfg.getMode();
399 
400     // Create list of environment variables. This list must be part of the
401     // returned structure everytimes ... but some of its members are opetional!
402     css::uno::Sequence< css::beans::NamedValue > lEnvArgs(1);
403     lEnvArgs[0].Name    = ::rtl::OUString::createFromAscii(JobData::PROP_ENVTYPE);
404     lEnvArgs[0].Value <<= m_aJobCfg.getEnvironmentDescriptor();
405 
406     if (m_xFrame.is())
407     {
408         sal_Int32 c = lEnvArgs.getLength();
409         lEnvArgs.realloc(c+1);
410         lEnvArgs[c].Name    = ::rtl::OUString::createFromAscii(JobData::PROP_FRAME);
411         lEnvArgs[c].Value <<= m_xFrame;
412     }
413     if (m_xModel.is())
414     {
415         sal_Int32 c = lEnvArgs.getLength();
416         lEnvArgs.realloc(c+1);
417         lEnvArgs[c].Name    = ::rtl::OUString::createFromAscii(JobData::PROP_MODEL);
418         lEnvArgs[c].Value <<= m_xModel;
419     }
420     if (eMode==JobData::E_EVENT)
421     {
422         sal_Int32 c = lEnvArgs.getLength();
423         lEnvArgs.realloc(c+1);
424         lEnvArgs[c].Name    = ::rtl::OUString::createFromAscii(JobData::PROP_EVENTNAME);
425         lEnvArgs[c].Value <<= m_aJobCfg.getEvent();
426     }
427 
428     // get the configuration data from the job data container ... if possible
429     // Means: if this job has any configuration data. Note: only realy
430     // filled lists will be set to the return structure at the end of this method.
431     css::uno::Sequence< css::beans::NamedValue > lConfigArgs   ;
432     css::uno::Sequence< css::beans::NamedValue > lJobConfigArgs;
433     if (eMode==JobData::E_ALIAS || eMode==JobData::E_EVENT)
434     {
435         lConfigArgs    = m_aJobCfg.getConfig();
436         lJobConfigArgs = m_aJobCfg.getJobConfig();
437     }
438 
439     aReadLock.unlock();
440     /* } SAFE */
441 
442     // Add all valid (not empty) lists to the return list
443     if (lConfigArgs.getLength()>0)
444     {
445         sal_Int32 nLength = lAllArgs.getLength();
446         lAllArgs.realloc(nLength+1);
447         lAllArgs[nLength].Name    = ::rtl::OUString::createFromAscii(JobData::PROPSET_CONFIG);
448         lAllArgs[nLength].Value <<= lConfigArgs;
449     }
450     if (lJobConfigArgs.getLength()>0)
451     {
452         sal_Int32 nLength = lAllArgs.getLength();
453         lAllArgs.realloc(nLength+1);
454         lAllArgs[nLength].Name    = ::rtl::OUString::createFromAscii(JobData::PROPSET_OWNCONFIG);
455         lAllArgs[nLength].Value <<= lJobConfigArgs;
456     }
457     if (lEnvArgs.getLength()>0)
458     {
459         sal_Int32 nLength = lAllArgs.getLength();
460         lAllArgs.realloc(nLength+1);
461         lAllArgs[nLength].Name    = ::rtl::OUString::createFromAscii(JobData::PROPSET_ENVIRONMENT);
462         lAllArgs[nLength].Value <<= lEnvArgs;
463     }
464     if (lDynamicArgs.getLength()>0)
465     {
466         sal_Int32 nLength = lAllArgs.getLength();
467         lAllArgs.realloc(nLength+1);
468         lAllArgs[nLength].Name    = ::rtl::OUString::createFromAscii(JobData::PROPSET_DYNAMICDATA);
469         lAllArgs[nLength].Value <<= lDynamicArgs;
470     }
471 
472     return lAllArgs;
473 }
474 
475 //________________________________
476 /**
477     @short  analyze the given job result and change the job configuration
478     @descr  Note: Some results can be handled only, if this job has a valid configuration!
479             For "not configured jobs" (means pure services) they can be ignored.
480             But these cases are handled by our JobData member. We can call it everytime.
481             It does the right things automaticly. E.g. if the job has no configuration ...
482             it does nothing during setJobConfig()!
483 
484     @param  aResult
485                 the job result for analyzing
486 */
487 void Job::impl_reactForJobResult( /*IN*/ const css::uno::Any& aResult )
488 {
489     /* SAFE { */
490     WriteGuard aWriteLock(m_aLock);
491 
492     // analyze the result set ...
493     JobResult aAnalyzedResult(aResult);
494 
495     // some of the following operations will be supported for different environments
496     // or different type of jobs only.
497     JobData::EEnvironment eEnvironment = m_aJobCfg.getEnvironment();
498 
499     // write back the job specific configuration data ...
500     // If the environment allow it and if this job has a configuration!
501     if (
502         (m_aJobCfg.hasConfig()                            ) &&
503         (aAnalyzedResult.existPart(JobResult::E_ARGUMENTS))
504        )
505     {
506         m_aJobCfg.setJobConfig(aAnalyzedResult.getArguments());
507     }
508 
509     // disable a job for further executions.
510     // Note: this option is available inside the environment EXECUTOR only
511     if (
512 //        (eEnvironment == JobData::E_EXECUTION              ) &&
513         (m_aJobCfg.hasConfig()                             ) &&
514         (aAnalyzedResult.existPart(JobResult::E_DEACTIVATE))
515        )
516     {
517         m_aJobCfg.disableJob();
518     }
519 
520     // notify any interested listener with the may given result state.
521     // Note: this option is available inside the environment DISPATCH only
522     if (
523         (eEnvironment == JobData::E_DISPATCH                   ) &&
524         (m_xResultListener.is()                                ) &&
525         (aAnalyzedResult.existPart(JobResult::E_DISPATCHRESULT))
526        )
527     {
528         m_aJobCfg.setResult(aAnalyzedResult);
529         // Attention: Because the listener expect that the original object send this event ...
530         // and we nor the job are the right ones ...
531         // our user has set itself before. So we can fake this source address!
532         css::frame::DispatchResultEvent aEvent        = aAnalyzedResult.getDispatchResult();
533                                         aEvent.Source = m_xResultSourceFake;
534         m_xResultListener->dispatchFinished(aEvent);
535     }
536 
537     aWriteLock.unlock();
538     /* SAFE { */
539 }
540 
541 //________________________________
542 /**
543     @short  starts listening for office shutdown and closing of our
544             given target frame (if its a valid reference)
545     @descr  We will reghister ourself as terminate listener
546             at the global desktop instance. That will hold us
547             alive and additional we get the information, if the
548             office whish to shutdown. If then an internal job
549             is running we will have the chance to supress that
550             by throwing a veto exception. If our internal wrapped
551             job finished his work, we can release this listener
552             connection.
553 
554             Further we are listener for closing of the (possible valid)
555             given frame. We must be shure, that this ressource won't be gone
556             if our internal job is still running.
557 */
558 void Job::impl_startListening()
559 {
560     /* SAFE { */
561     WriteGuard aWriteLock(m_aLock);
562 
563     // listening for office shutdown
564     if (!m_xDesktop.is() && !m_bListenOnDesktop)
565     {
566         try
567         {
568             m_xDesktop = css::uno::Reference< css::frame::XDesktop >(m_xSMGR->createInstance(SERVICENAME_DESKTOP), css::uno::UNO_QUERY);
569             css::uno::Reference< css::frame::XTerminateListener > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY);
570             if (m_xDesktop.is())
571             {
572                 m_xDesktop->addTerminateListener(xThis);
573                 m_bListenOnDesktop = sal_True;
574             }
575         }
576         catch(css::uno::Exception&)
577         {
578             m_xDesktop = css::uno::Reference< css::frame::XDesktop >();
579         }
580     }
581 
582     // listening for frame closing
583     if (m_xFrame.is() && !m_bListenOnFrame)
584     {
585         try
586         {
587             css::uno::Reference< css::util::XCloseBroadcaster > xCloseable(m_xFrame                                 , css::uno::UNO_QUERY);
588             css::uno::Reference< css::util::XCloseListener >    xThis     (static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY);
589             if (xCloseable.is())
590             {
591                 xCloseable->addCloseListener(xThis);
592                 m_bListenOnFrame = sal_True;
593             }
594         }
595         catch(css::uno::Exception&)
596         {
597             m_bListenOnFrame = sal_False;
598         }
599     }
600 
601     // listening for model closing
602     if (m_xModel.is() && !m_bListenOnModel)
603     {
604         try
605         {
606             css::uno::Reference< css::util::XCloseBroadcaster > xCloseable(m_xModel                                 , css::uno::UNO_QUERY);
607             css::uno::Reference< css::util::XCloseListener >    xThis     (static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY);
608             if (xCloseable.is())
609             {
610                 xCloseable->addCloseListener(xThis);
611                 m_bListenOnModel = sal_True;
612             }
613         }
614         catch(css::uno::Exception&)
615         {
616             m_bListenOnModel = sal_False;
617         }
618     }
619 
620     aWriteLock.unlock();
621     /* } SAFE */
622 }
623 
624 //________________________________
625 /**
626     @short  release listener connection for office shutdown
627     @descr  see description of impl_startListening()
628 */
629 void Job::impl_stopListening()
630 {
631     /* SAFE { */
632     WriteGuard aWriteLock(m_aLock);
633 
634     // stop listening for office shutdown
635     if (m_xDesktop.is() && m_bListenOnDesktop)
636     {
637         try
638         {
639             css::uno::Reference< css::frame::XTerminateListener > xThis(static_cast< ::cppu::OWeakObject* >(this)   , css::uno::UNO_QUERY);
640             m_xDesktop->removeTerminateListener(xThis);
641             m_xDesktop = css::uno::Reference< css::frame::XDesktop >();
642             m_bListenOnDesktop = sal_False;
643         }
644         catch(css::uno::Exception&)
645         {
646         }
647     }
648 
649     // stop listening for frame closing
650     if (m_xFrame.is() && m_bListenOnFrame)
651     {
652         try
653         {
654             css::uno::Reference< css::util::XCloseBroadcaster > xCloseable(m_xFrame                                 , css::uno::UNO_QUERY);
655             css::uno::Reference< css::util::XCloseListener >    xThis     (static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY);
656             if (xCloseable.is())
657             {
658                 xCloseable->removeCloseListener(xThis);
659                 m_bListenOnFrame = sal_False;
660             }
661         }
662         catch(css::uno::Exception&)
663         {
664         }
665     }
666 
667     // stop listening for model closing
668     if (m_xModel.is() && m_bListenOnModel)
669     {
670         try
671         {
672             css::uno::Reference< css::util::XCloseBroadcaster > xCloseable(m_xModel                                 , css::uno::UNO_QUERY);
673             css::uno::Reference< css::util::XCloseListener >    xThis     (static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY);
674             if (xCloseable.is())
675             {
676                 xCloseable->removeCloseListener(xThis);
677                 m_bListenOnModel = sal_False;
678             }
679         }
680         catch(css::uno::Exception&)
681         {
682         }
683     }
684 
685     aWriteLock.unlock();
686     /* } SAFE */
687 }
688 
689 //________________________________
690 /**
691     @short  callback from any asynchronous executed job
692 
693     @descr  Our execute() method waits for this callback.
694             We have to react for the possible results here,
695             to kill the running job and disable the blocked condition
696             so execute() can be finished too.
697 
698     @param  xJob
699                 the job, which was running and inform us now
700 
701     @param  aResult
702                 it's results
703 */
704 void SAL_CALL Job::jobFinished( /*IN*/ const css::uno::Reference< css::task::XAsyncJob >& xJob    ,
705                                 /*IN*/ const css::uno::Any&                               aResult ) throw(css::uno::RuntimeException)
706 {
707     /* SAFE { */
708     WriteGuard aWriteLock(m_aLock);
709 
710     // It's neccessary to check this.
711     // May this job was cancelled by any other reason
712     // some milliseconds before. :-)
713     if (m_xJob.is() && m_xJob==xJob)
714     {
715         // react for his results
716         // (means enable/disable it for further requests
717         // or save arguments or notify listener ...)
718         impl_reactForJobResult(aResult);
719 
720         // Let the job die!
721         m_xJob = css::uno::Reference< css::uno::XInterface >();
722     }
723 
724     // And let the start method "execute()" finishing it's job.
725     // But do it everytime. So any outside blocking code can finish
726     // his work too.
727     m_aAsyncWait.set();
728 
729     aWriteLock.unlock();
730     /* } SAFE */
731 }
732 
733 //________________________________
734 /**
735     @short  prevent internal wrapped job against office termination
736     @descr  This event is broadcasted by the desktop instance and ask for an office termination.
737             If the internal wrapped job is still in progress, we disagree with that by throwing the
738             right veto exception. If not - we agree. But then we must be aware, that another event
739             notifyTermination() can follow. Then we have no chance to do the same. Then we have to
740             accept that and stop our work instandly.
741 
742     @param  aEvent
743                 describes the broadcaster and must be the desktop instance
744 
745     @throw  TerminateVetoException
746                 if our internal wrapped job is still running.
747  */
748 void SAL_CALL Job::queryTermination( /*IN*/ const css::lang::EventObject& ) throw(css::frame::TerminationVetoException,
749                                                                                          css::uno::RuntimeException          )
750 {
751     /* SAFE { */
752     ReadGuard aReadLock(m_aLock);
753 
754     // don't disagree with this request if job was already stopped or finished it's work
755     // if (m_eRunState != E_RUNNING)
756     //    return;
757 
758     // Otherwhise try to close() it
759     css::uno::Reference< css::util::XCloseable > xClose(m_xJob, css::uno::UNO_QUERY);
760     if (xClose.is())
761     {
762         try
763         {
764             xClose->close(sal_False);
765             m_eRunState = E_STOPPED_OR_FINISHED;
766         }
767         catch(const css::util::CloseVetoException&) {}
768     }
769 
770     if (m_eRunState != E_STOPPED_OR_FINISHED)
771     {
772         css::uno::Reference< css::uno::XInterface > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY);
773         throw css::frame::TerminationVetoException(DECLARE_ASCII("job still in progress"), xThis);
774     }
775 
776     aReadLock.unlock();
777     /* } SAFE */
778 }
779 
780 
781 //________________________________
782 /**
783     @short  inform us about office termination
784     @descr  Instead of the method queryTermination(), here is no chance to disagree with that.
785             We have to accept it and cancel all current processes inside.
786             It can occure only, if job was not already started if queryTermination() was called here ..
787             Then we had not throwed a veto exception. But now we must agree with this situation and break
788             all our internal processes. Its not a good idea to mark this instance as non startable any longer
789             inside queryTermination() if no job was unning too. Because that would disable this job and may
790             the office does not realy shutdownm, because another listener has thrown the suitable exception.
791 
792     @param  aEvent
793                 describes the broadcaster and must be the desktop instance
794  */
795 void SAL_CALL Job::notifyTermination( /*IN*/ const css::lang::EventObject& ) throw(css::uno::RuntimeException)
796 {
797     die();
798     // Do nothing else here. Our internal ressources was released ...
799 }
800 
801 //________________________________
802 /**
803     @short  prevent internal wrapped job against frame closing
804     @descr  This event is broadcasted by the frame instance and ask for closing.
805             If the internal wrapped job is still in progress, we disagree with that by throwing the
806             right veto exception. If not - we agree. But then we must be aware, that another event
807             notifyClosing() can follow. Then we have no chance to do the same. Then we have to
808             accept that and stop our work instandly.
809 
810     @param  aEvent
811                 describes the broadcaster and must be the frame instance
812 
813     @param  bGetsOwnerShip
814                 If it's set to <sal_True> and we throw the right veto excepion, we have to close this frame later
815                 if our internal processes will be finished. If it's set to <FALSE/> we can ignore it.
816 
817     @throw  CloseVetoException
818                 if our internal wrapped job is still running.
819  */
820 void SAL_CALL Job::queryClosing( const css::lang::EventObject& aEvent         ,
821                                        sal_Bool                bGetsOwnership ) throw(css::util::CloseVetoException,
822                                                                                       css::uno::RuntimeException   )
823 {
824     /* SAFE { */
825     WriteGuard aWriteLock(m_aLock);
826 
827     // do nothing, if no internal job is still running ...
828     // The frame or model can be closed then successfully.
829     if (m_eRunState != E_RUNNING)
830         return;
831 
832     // try close() first at the job.
833     // The job can agree or disagree with this request.
834     css::uno::Reference< css::util::XCloseable > xClose(m_xJob, css::uno::UNO_QUERY);
835     if (xClose.is())
836     {
837         xClose->close(bGetsOwnership);
838         // Here we can say: "this job was stopped successfully". Because
839         // no veto exception was thrown!
840         m_eRunState = E_STOPPED_OR_FINISHED;
841         return;
842     }
843 
844     // try dispose() then
845     // Here the job has no chance for a veto.
846     // But we must be aware of an "already disposed exception"...
847     try
848     {
849         css::uno::Reference< css::lang::XComponent > xDispose(m_xJob, css::uno::UNO_QUERY);
850         if (xDispose.is())
851         {
852             xDispose->dispose();
853             m_eRunState = E_DISPOSED;
854         }
855     }
856     catch(const css::lang::DisposedException&)
857     {
858         // the job was already disposed by any other mechanism !?
859         // But it's not interesting for us. For us this job is stopped now.
860         m_eRunState = E_DISPOSED;
861     }
862 
863     if (m_eRunState != E_DISPOSED)
864     {
865         // analyze event source - to find out, which resource called queryClosing() at this
866         // job wrapper. We must bind a "pending close" request to this resource.
867         // Closing of the corresponding resource will be done if our internal job finish it's work.
868         m_bPendingCloseFrame = (m_xFrame.is() && aEvent.Source == m_xFrame);
869         m_bPendingCloseModel = (m_xModel.is() && aEvent.Source == m_xModel);
870 
871         // throw suitable veto exception - because the internal job could not be cancelled.
872         css::uno::Reference< css::uno::XInterface > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY);
873         throw css::util::CloseVetoException(DECLARE_ASCII("job still in progress"), xThis);
874     }
875 
876     // No veto ...
877     // But don't call die() here or free our internal member.
878     // This must be done inside notifyClosing() only. Otherwhise the
879     // might stopped job has no chance to return it's results or
880     // call us back. We must give him the chance to finish it's work successfully.
881 
882     aWriteLock.unlock();
883     /* } SAFE */
884 }
885 
886 //________________________________
887 /**
888     @short  inform us about frame closing
889     @descr  Instead of the method queryClosing(), here is no chance to disagree with that.
890             We have to accept it and cancel all current processes inside.
891 
892     @param  aEvent
893             describes the broadcaster and must be the frame or model instance we know
894  */
895 void SAL_CALL Job::notifyClosing( const css::lang::EventObject& ) throw(css::uno::RuntimeException)
896 {
897     die();
898     // Do nothing else here. Our internal ressources was released ...
899 }
900 
901 //________________________________
902 /**
903     @short      shouldn't be called normaly
904     @descr      But it doesn't matter, who called it. We have to kill our internal
905                 running processes hardly.
906 
907     @param      aEvent
908                 describe the broadcaster
909 */
910 void SAL_CALL Job::disposing( const css::lang::EventObject& aEvent ) throw(css::uno::RuntimeException)
911 {
912     /* SAFE { */
913     WriteGuard aWriteLock(m_aLock);
914 
915     if (m_xDesktop.is() && aEvent.Source == m_xDesktop)
916     {
917         m_xDesktop = css::uno::Reference< css::frame::XDesktop >();
918         m_bListenOnDesktop = sal_False;
919     }
920     else
921     if (m_xFrame.is() && aEvent.Source == m_xFrame)
922     {
923         m_xFrame = css::uno::Reference< css::frame::XFrame >();
924         m_bListenOnFrame = sal_False;
925     }
926     else
927     if (m_xModel.is() && aEvent.Source == m_xModel)
928     {
929         m_xModel = css::uno::Reference< css::frame::XModel >();
930         m_bListenOnModel = sal_False;
931     }
932 
933     aWriteLock.unlock();
934     /* } SAFE */
935 
936     die();
937     // Do nothing else here. Our internal ressources was released ...
938 }
939 
940 } // namespace framework
941