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
DEFINE_XINTERFACE_4(Job,OWeakObject,DIRECT_INTERFACE (css::lang::XTypeProvider),DIRECT_INTERFACE (css::task::XJobListener),DIRECT_INTERFACE (css::frame::XTerminateListener),DIRECT_INTERFACE (css::util::XCloseListener))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 initializes this new instance. But it sets some generic parameters here only.
81 Specialized informations (e.g. the alias or service name of this 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 initializes this new instance. But it sets some generic parameters here only.
111 Specialized informations (e.g. the alias or service name of this 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 */
Job(const css::uno::Reference<css::lang::XMultiServiceFactory> & xSMGR,const css::uno::Reference<css::frame::XModel> & xModel)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 superfluous!
140 @descr Releasing of memory and reference must be done inside die() call.
141 Otherwise it's a bug.
142 */
~Job()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 */
setDispatchResultFake(const css::uno::Reference<css::frame::XDispatchResultListener> & xListener,const css::uno::Reference<css::uno::XInterface> & xSourceFake)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
setJobData(const JobData & aData)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 canceled.
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 */
execute(const css::uno::Sequence<css::beans::NamedValue> & lDynamicArgs)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 necessary to hold us self alive!
230 // Otherwise 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 prefer 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 asynchronous
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 synchronous
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 every activity. After doing so - this instance will not be
334 usable any longer! Of course we try to handle further requests
335 carefully. Maybe somewhere else hold a reference to us ...
336 */
die()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) its static configuration data (Equals for all jobs. )
379 b) its 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 */
impl_generateJobArgs(const css::uno::Sequence<css::beans::NamedValue> & lDynamicArgs)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 every time ... but some of its members are optional!
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 really
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 every time.
481 It does the right things automatically. 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 */
impl_reactForJobResult(const css::uno::Any & aResult)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 register 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 wishes to shutdown. If then an internal job
549 is running we will have the chance to suppress 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 sure, that this resource won't be gone
556 if our internal job is still running.
557 */
impl_startListening()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 */
impl_stopListening()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 its results
703 */
jobFinished(const css::uno::Reference<css::task::XAsyncJob> & xJob,const css::uno::Any & aResult)704 void SAL_CALL Job::jobFinished( /*IN*/ const css::uno::Reference< css::task::XAsyncJob >& xJob ,
705 /*IN*/ const css::uno::Any& aResult )
706 {
707 /* SAFE { */
708 WriteGuard aWriteLock(m_aLock);
709
710 // It's necessary to check this.
711 // May this job was canceled 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 its job.
725 // But do it every time. 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 instantly.
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 */
queryTermination(const css::lang::EventObject &)748 void SAL_CALL Job::queryTermination( /*IN*/ const css::lang::EventObject& )
749 {
750 /* SAFE { */
751 ReadGuard aReadLock(m_aLock);
752
753 // don't disagree with this request if job was already stopped or finished its work
754 // if (m_eRunState != E_RUNNING)
755 // return;
756
757 // Otherwise try to close() it
758 css::uno::Reference< css::util::XCloseable > xClose(m_xJob, css::uno::UNO_QUERY);
759 if (xClose.is())
760 {
761 try
762 {
763 xClose->close(sal_False);
764 m_eRunState = E_STOPPED_OR_FINISHED;
765 }
766 catch(const css::util::CloseVetoException&) {}
767 }
768
769 if (m_eRunState != E_STOPPED_OR_FINISHED)
770 {
771 css::uno::Reference< css::uno::XInterface > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY);
772 throw css::frame::TerminationVetoException(DECLARE_ASCII("job still in progress"), xThis);
773 }
774
775 aReadLock.unlock();
776 /* } SAFE */
777 }
778
779
780 //________________________________
781 /**
782 @short inform us about office termination
783 @descr Instead of the method queryTermination(), here is no chance to disagree with that.
784 We have to accept it and cancel all current processes inside.
785 It can occur only if job was not already started if queryTermination() was called here ..
786 Then we had not thrown a veto exception. But now we must agree with this situation and break
787 all our internal processes. Its not a good idea to mark this instance as non startable any longer
788 inside queryTermination() if no job was running too. Because that would disable this job and may
789 the office does not really shutdown, because another listener has thrown the suitable exception.
790
791 @param aEvent
792 describes the broadcaster and must be the desktop instance
793 */
notifyTermination(const css::lang::EventObject &)794 void SAL_CALL Job::notifyTermination( /*IN*/ const css::lang::EventObject& )
795 {
796 die();
797 // Do nothing else here. Our internal resources was released ...
798 }
799
800 //________________________________
801 /**
802 @short prevent internal wrapped job against frame closing
803 @descr This event is broadcasted by the frame instance and ask for closing.
804 If the internal wrapped job is still in progress, we disagree with that by throwing the
805 right veto exception. If not - we agree. But then we must be aware, that another event
806 notifyClosing() can follow. Then we have no chance to do the same. Then we have to
807 accept that and stop our work instantly.
808
809 @param aEvent
810 describes the broadcaster and must be the frame instance
811
812 @param bGetsOwnerShip
813 If it's set to <sal_True> and we throw the right veto exception, we have to close this frame later
814 if our internal processes will be finished. If it's set to <FALSE/> we can ignore it.
815
816 @throw CloseVetoException
817 if our internal wrapped job is still running.
818 */
queryClosing(const css::lang::EventObject & aEvent,sal_Bool bGetsOwnership)819 void SAL_CALL Job::queryClosing( const css::lang::EventObject& aEvent ,
820 sal_Bool bGetsOwnership )
821 {
822 /* SAFE { */
823 WriteGuard aWriteLock(m_aLock);
824
825 // do nothing, if no internal job is still running ...
826 // The frame or model can be closed then successfully.
827 if (m_eRunState != E_RUNNING)
828 return;
829
830 // try close() first at the job.
831 // The job can agree or disagree with this request.
832 css::uno::Reference< css::util::XCloseable > xClose(m_xJob, css::uno::UNO_QUERY);
833 if (xClose.is())
834 {
835 xClose->close(bGetsOwnership);
836 // Here we can say: "this job was stopped successfully". Because
837 // no veto exception was thrown!
838 m_eRunState = E_STOPPED_OR_FINISHED;
839 return;
840 }
841
842 // try dispose() then
843 // Here the job has no chance for a veto.
844 // But we must be aware of an "already disposed exception"...
845 try
846 {
847 css::uno::Reference< css::lang::XComponent > xDispose(m_xJob, css::uno::UNO_QUERY);
848 if (xDispose.is())
849 {
850 xDispose->dispose();
851 m_eRunState = E_DISPOSED;
852 }
853 }
854 catch(const css::lang::DisposedException&)
855 {
856 // the job was already disposed by any other mechanism !?
857 // But it's not interesting for us. For us this job is stopped now.
858 m_eRunState = E_DISPOSED;
859 }
860
861 if (m_eRunState != E_DISPOSED)
862 {
863 // analyze event source - to find out, which resource called queryClosing() at this
864 // job wrapper. We must bind a "pending close" request to this resource.
865 // Closing of the corresponding resource will be done if our internal job finish its work.
866 m_bPendingCloseFrame = (m_xFrame.is() && aEvent.Source == m_xFrame);
867 m_bPendingCloseModel = (m_xModel.is() && aEvent.Source == m_xModel);
868
869 // throw suitable veto exception - because the internal job could not be cancelled.
870 css::uno::Reference< css::uno::XInterface > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY);
871 throw css::util::CloseVetoException(DECLARE_ASCII("job still in progress"), xThis);
872 }
873
874 // No veto ...
875 // But don't call die() here or free our internal member.
876 // This must be done inside notifyClosing() only. Otherwise the
877 // might stopped job has no chance to return its results or
878 // call us back. We must give him the chance to finish its work successfully.
879
880 aWriteLock.unlock();
881 /* } SAFE */
882 }
883
884 //________________________________
885 /**
886 @short inform us about frame closing
887 @descr Instead of the method queryClosing(), here is no chance to disagree with that.
888 We have to accept it and cancel all current processes inside.
889
890 @param aEvent
891 describes the broadcaster and must be the frame or model instance we know
892 */
notifyClosing(const css::lang::EventObject &)893 void SAL_CALL Job::notifyClosing( const css::lang::EventObject& )
894 {
895 die();
896 // Do nothing else here. Our internal resource was released ...
897 }
898
899 //________________________________
900 /**
901 @short shouldn't be called normally
902 @descr But it doesn't matter, who called it. We have to kill our internal
903 running processes hardly.
904
905 @param aEvent
906 describe the broadcaster
907 */
disposing(const css::lang::EventObject & aEvent)908 void SAL_CALL Job::disposing( const css::lang::EventObject& aEvent )
909 {
910 /* SAFE { */
911 WriteGuard aWriteLock(m_aLock);
912
913 if (m_xDesktop.is() && aEvent.Source == m_xDesktop)
914 {
915 m_xDesktop = css::uno::Reference< css::frame::XDesktop >();
916 m_bListenOnDesktop = sal_False;
917 }
918 else
919 if (m_xFrame.is() && aEvent.Source == m_xFrame)
920 {
921 m_xFrame = css::uno::Reference< css::frame::XFrame >();
922 m_bListenOnFrame = sal_False;
923 }
924 else
925 if (m_xModel.is() && aEvent.Source == m_xModel)
926 {
927 m_xModel = css::uno::Reference< css::frame::XModel >();
928 m_bListenOnModel = sal_False;
929 }
930
931 aWriteLock.unlock();
932 /* } SAFE */
933
934 die();
935 // Do nothing else here. Our internal resource was released ...
936 }
937
938 } // namespace framework
939