LibreOffice Module comphelper (master)  1
instancelocker.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  * Licensed to the Apache Software Foundation (ASF) under one or more
12  * contributor license agreements. See the NOTICE file distributed
13  * with this work for additional information regarding copyright
14  * ownership. The ASF licenses this file to you under the Apache
15  * License, Version 2.0 (the "License"); you may not use this file
16  * except in compliance with the License. You may obtain a copy of
17  * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 
23 
24 #include <com/sun/star/util/CloseVetoException.hpp>
25 #include <com/sun/star/util/XCloseBroadcaster.hpp>
26 #include <com/sun/star/util/XCloseable.hpp>
27 #include <com/sun/star/lang/DisposedException.hpp>
28 #include <com/sun/star/lang/IllegalArgumentException.hpp>
29 #include <com/sun/star/frame/XDesktop.hpp>
30 #include <com/sun/star/frame/TerminationVetoException.hpp>
31 #include <com/sun/star/frame/DoubleInitializationException.hpp>
32 #include <com/sun/star/embed/Actions.hpp>
33 #include <com/sun/star/embed/XActionsApproval.hpp>
34 
35 #include "instancelocker.hxx"
36 
37 namespace com::sun::star::uno { class XComponentContext; }
38 
39 using namespace ::com::sun::star;
40 
41 
42 // OInstanceLocker
43 
44 
46 : m_bDisposed( false )
47 , m_bInitialized( false )
48 {
49 }
50 
51 
53 {
54  if ( !m_bDisposed )
55  {
56  osl_atomic_increment(&m_refCount); // to call dispose
57  try {
58  dispose();
59  }
60  catch ( uno::RuntimeException& )
61  {}
62  }
63 }
64 
65 // XComponent
66 
67 void SAL_CALL OInstanceLocker::dispose()
68 {
69  std::unique_lock aGuard( m_aMutex );
70 
71  if ( m_bDisposed )
72  throw lang::DisposedException();
73 
74  lang::EventObject aSource( static_cast< ::cppu::OWeakObject* >(this) );
75  m_aListenersContainer.disposeAndClear( aGuard, aSource );
76  aGuard.lock();
77  if ( m_xLockListener.is() )
78  {
79  auto tmp = std::move(m_xLockListener);
80  aGuard.unlock();
81  tmp->Dispose();
82  aGuard.lock();
83  }
84 
85  m_bDisposed = true;
86 }
87 
88 
89 void SAL_CALL OInstanceLocker::addEventListener( const uno::Reference< lang::XEventListener >& xListener )
90 {
91  std::scoped_lock aGuard( m_aMutex );
92  if ( m_bDisposed )
93  throw lang::DisposedException(); // TODO
94 
96 }
97 
98 
99 void SAL_CALL OInstanceLocker::removeEventListener( const uno::Reference< lang::XEventListener >& xListener )
100 {
101  std::scoped_lock aGuard( m_aMutex );
103 }
104 
105 // XInitialization
106 
107 void SAL_CALL OInstanceLocker::initialize( const uno::Sequence< uno::Any >& aArguments )
108 {
109  std::unique_lock aGuard( m_aMutex );
110  if ( m_bInitialized )
111  throw frame::DoubleInitializationException();
112 
113  if ( m_bDisposed )
114  throw lang::DisposedException(); // TODO
115 
116  if ( !m_refCount )
117  throw uno::RuntimeException(); // the object must be refcounted already!
118 
119  uno::Reference< uno::XInterface > xInstance;
120  uno::Reference< embed::XActionsApproval > xApproval;
121 
122  try
123  {
124  sal_Int32 nLen = aArguments.getLength();
125  if ( nLen < 2 || nLen > 3 )
126  throw lang::IllegalArgumentException(
127  "Wrong count of parameters!",
128  uno::Reference< uno::XInterface >(),
129  0 );
130 
131  if ( !( aArguments[0] >>= xInstance ) || !xInstance.is() )
132  throw lang::IllegalArgumentException(
133  "Nonempty reference is expected as the first argument!",
134  uno::Reference< uno::XInterface >(),
135  0 );
136 
137  sal_Int32 nModes = 0;
138  if (
139  !( aArguments[1] >>= nModes ) ||
140  (
141  !( nModes & embed::Actions::PREVENT_CLOSE ) &&
142  !( nModes & embed::Actions::PREVENT_TERMINATION )
143  )
144  )
145  {
146  throw lang::IllegalArgumentException(
147  "The correct modes set is expected as the second argument!",
148  uno::Reference< uno::XInterface >(),
149  0 );
150  }
151 
152  if ( nLen == 3 && !( aArguments[2] >>= xApproval ) )
153  throw lang::IllegalArgumentException(
154  "If the third argument is provided, it must be XActionsApproval implementation!",
155  uno::Reference< uno::XInterface >(),
156  0 );
157 
158  m_xLockListener = new OLockListener( uno::Reference< lang::XComponent > ( static_cast< lang::XComponent* >( this ) ),
159  xInstance,
160  nModes,
161  xApproval );
162  m_xLockListener->Init();
163  }
164  catch( uno::Exception& )
165  {
166  aGuard.unlock();
167  dispose();
168  throw;
169  }
170 
171  m_bInitialized = true;
172 }
173 
174 // XServiceInfo
176 {
177  return "com.sun.star.comp.embed.InstanceLocker";
178 }
179 
180 sal_Bool SAL_CALL OInstanceLocker::supportsService( const OUString& ServiceName )
181 {
182  return cppu::supportsService(this, ServiceName);
183 }
184 
185 uno::Sequence< OUString > SAL_CALL OInstanceLocker::getSupportedServiceNames()
186 {
187  return { "com.sun.star.embed.InstanceLocker" };
188 }
189 
190 // OLockListener
191 
192 
193 OLockListener::OLockListener( const uno::WeakReference< lang::XComponent >& xWrapper,
194  const uno::Reference< uno::XInterface >& xInstance,
195  sal_Int32 nMode,
196  const uno::Reference< embed::XActionsApproval >& rApproval )
197 : m_xInstance( xInstance )
198 , m_xApproval( rApproval )
199 , m_xWrapper( xWrapper )
200 , m_bDisposed( false )
201 , m_bInitialized( false )
202 , m_nMode( nMode )
203 {
204 }
205 
206 
208 {
209 }
210 
211 
213 {
214  osl::MutexGuard aGuard( m_aMutex );
215 
216  if ( m_bDisposed )
217  return;
218 
219  if ( m_nMode & embed::Actions::PREVENT_CLOSE )
220  {
221  try
222  {
223  uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( m_xInstance, uno::UNO_QUERY );
224  if ( xCloseBroadcaster.is() )
225  xCloseBroadcaster->removeCloseListener( static_cast< util::XCloseListener* >( this ) );
226 
227  uno::Reference< util::XCloseable > xCloseable( m_xInstance, uno::UNO_QUERY );
228  if ( xCloseable.is() )
229  xCloseable->close( true );
230  }
231  catch( uno::Exception& )
232  {}
233  }
234 
235  if ( m_nMode & embed::Actions::PREVENT_TERMINATION )
236  {
237  try
238  {
239  uno::Reference< frame::XDesktop > xDesktop( m_xInstance, uno::UNO_QUERY_THROW );
240  xDesktop->removeTerminateListener( static_cast< frame::XTerminateListener* >( this ) );
241  }
242  catch( uno::Exception& )
243  {}
244  }
245 
246  m_xInstance.clear();
247  m_bDisposed = true;
248 }
249 
250 // XEventListener
251 
252 void SAL_CALL OLockListener::disposing( const lang::EventObject& aEvent )
253 {
254  osl::ClearableMutexGuard aGuard( m_aMutex );
255 
256  // object is disposed
257  if ( aEvent.Source != m_xInstance )
258  return;
259 
260  // the object does not listen for anything any more
261  m_nMode = 0;
262 
263  // dispose the wrapper;
264  uno::Reference< lang::XComponent > xComponent( m_xWrapper.get(), uno::UNO_QUERY );
265  aGuard.clear();
266  if ( xComponent.is() )
267  {
268  try { xComponent->dispose(); }
269  catch( uno::Exception& ){}
270  }
271 }
272 
273 
274 // XCloseListener
275 
276 void SAL_CALL OLockListener::queryClosing( const lang::EventObject& aEvent, sal_Bool )
277 {
278  // GetsOwnership parameter is always ignored, the user of the service must close the object always
279  osl::ClearableMutexGuard aGuard( m_aMutex );
280  if ( !(!m_bDisposed && aEvent.Source == m_xInstance && ( m_nMode & embed::Actions::PREVENT_CLOSE )) )
281  return;
282 
283  try
284  {
285  uno::Reference< embed::XActionsApproval > xApprove = m_xApproval;
286 
287  // unlock the mutex here
288  aGuard.clear();
289 
290  if ( xApprove.is() && xApprove->approveAction( embed::Actions::PREVENT_CLOSE ) )
291  throw util::CloseVetoException();
292  }
293  catch( util::CloseVetoException& )
294  {
295  // rethrow this exception
296  throw;
297  }
298  catch( uno::Exception& )
299  {
300  // no action should be done
301  }
302 }
303 
304 
305 void SAL_CALL OLockListener::notifyClosing( const lang::EventObject& aEvent )
306 {
307  osl::ClearableMutexGuard aGuard( m_aMutex );
308 
309  // object is closed, no reason to listen
310  if ( aEvent.Source != m_xInstance )
311  return;
312 
313  uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( aEvent.Source, uno::UNO_QUERY );
314  if ( !xCloseBroadcaster.is() )
315  return;
316 
317  xCloseBroadcaster->removeCloseListener( static_cast< util::XCloseListener* >( this ) );
318  m_nMode &= ~embed::Actions::PREVENT_CLOSE;
319  if ( !m_nMode )
320  {
321  // dispose the wrapper;
322  uno::Reference< lang::XComponent > xComponent( m_xWrapper.get(), uno::UNO_QUERY );
323  aGuard.clear();
324  if ( xComponent.is() )
325  {
326  try { xComponent->dispose(); }
327  catch( uno::Exception& ){}
328  }
329  }
330 }
331 
332 
333 // XTerminateListener
334 
335 void SAL_CALL OLockListener::queryTermination( const lang::EventObject& aEvent )
336 {
337  osl::ClearableMutexGuard aGuard( m_aMutex );
338  if ( !(aEvent.Source == m_xInstance && ( m_nMode & embed::Actions::PREVENT_TERMINATION )) )
339  return;
340 
341  try
342  {
343  uno::Reference< embed::XActionsApproval > xApprove = m_xApproval;
344 
345  // unlock the mutex here
346  aGuard.clear();
347 
348  if ( xApprove.is() && xApprove->approveAction( embed::Actions::PREVENT_TERMINATION ) )
349  throw frame::TerminationVetoException();
350  }
351  catch( frame::TerminationVetoException& )
352  {
353  // rethrow this exception
354  throw;
355  }
356  catch( uno::Exception& )
357  {
358  // no action should be done
359  }
360 }
361 
362 
363 void SAL_CALL OLockListener::notifyTermination( const lang::EventObject& aEvent )
364 {
365  osl::ClearableMutexGuard aGuard( m_aMutex );
366 
367  // object is terminated, no reason to listen
368  if ( aEvent.Source != m_xInstance )
369  return;
370 
371  uno::Reference< frame::XDesktop > xDesktop( aEvent.Source, uno::UNO_QUERY );
372  if ( !xDesktop.is() )
373  return;
374 
375  try
376  {
377  xDesktop->removeTerminateListener( static_cast< frame::XTerminateListener* >( this ) );
378  m_nMode &= ~embed::Actions::PREVENT_TERMINATION;
379  if ( !m_nMode )
380  {
381  // dispose the wrapper;
382  uno::Reference< lang::XComponent > xComponent( m_xWrapper.get(), uno::UNO_QUERY );
383  aGuard.clear();
384  if ( xComponent.is() )
385  {
386  try { xComponent->dispose(); }
387  catch( uno::Exception& ){}
388  }
389  }
390  }
391  catch( uno::Exception& )
392  {}
393 }
394 
395 
396 // XInitialization
397 
399 {
400  osl::ClearableMutexGuard aGuard( m_aMutex );
401 
402  if ( m_bDisposed || m_bInitialized )
403  return;
404 
405  try
406  {
407  if ( m_nMode & embed::Actions::PREVENT_CLOSE )
408  {
409  uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( m_xInstance, uno::UNO_QUERY_THROW );
410  xCloseBroadcaster->addCloseListener( static_cast< util::XCloseListener* >( this ) );
411  }
412 
413  if ( m_nMode & embed::Actions::PREVENT_TERMINATION )
414  {
415  uno::Reference< frame::XDesktop > xDesktop( m_xInstance, uno::UNO_QUERY_THROW );
416  xDesktop->addTerminateListener( static_cast< frame::XTerminateListener* >( this ) );
417  }
418  }
419  catch( uno::Exception& )
420  {
421  // dispose the wrapper;
422  uno::Reference< lang::XComponent > xComponent( m_xWrapper.get(), uno::UNO_QUERY );
423  aGuard.clear();
424  if ( xComponent.is() )
425  {
426  try { xComponent->dispose(); }
427  catch( uno::Exception& ){}
428  }
429 
430  throw;
431  }
432 
433  m_bInitialized = true;
434 }
435 
436 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
438  css::uno::XComponentContext *,
439  css::uno::Sequence<css::uno::Any> const &)
440 {
441  return cppu::acquire(new OInstanceLocker());
442 }
443 
444 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void disposeAndClear(::std::unique_lock<::std::mutex > &rGuard, const css::lang::EventObject &rEvt)
Call disposing on all object in the container that support XEventListener.
virtual void SAL_CALL dispose() override
::osl::Mutex m_aMutex
css::uno::Reference< css::embed::XActionsApproval > m_xApproval
virtual void SAL_CALL notifyTermination(const css::lang::EventObject &Event) override
bool m_bDisposed
ULONG m_refCount
sal_Int32 m_nMode
exports com.sun.star. embed
virtual void SAL_CALL queryTermination(const css::lang::EventObject &Event) override
virtual void SAL_CALL disposing(const css::lang::EventObject &Source) override
virtual sal_Bool SAL_CALL supportsService(const OUString &ServiceName) override
virtual void SAL_CALL addEventListener(const css::uno::Reference< css::lang::XEventListener > &xListener) override
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
virtual void SAL_CALL initialize(const css::uno::Sequence< css::uno::Any > &aArguments) override
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * com_sun_star_comp_embed_InstanceLocker(css::uno::XComponentContext *, css::uno::Sequence< css::uno::Any > const &)
rtl::Reference< OLockListener > m_xLockListener
virtual OUString SAL_CALL getImplementationName() override
sal_Int32 removeInterface(const css::uno::Reference< ListenerT > &rxIFace)
Removes an element from the container.
unsigned char sal_Bool
virtual ~OInstanceLocker() override
sal_Int32 addInterface(const css::uno::Reference< ListenerT > &rxIFace)
Inserts an element into the container.
virtual void SAL_CALL notifyClosing(const css::lang::EventObject &Source) override
OLockListener(const css::uno::WeakReference< css::lang::XComponent > &xWrapper, const css::uno::Reference< css::uno::XInterface > &xInstance, sal_Int32 nMode, const css::uno::Reference< css::embed::XActionsApproval > &rApproval)
css::uno::Reference< css::uno::XInterface > m_xInstance
virtual void SAL_CALL removeEventListener(const css::uno::Reference< css::lang::XEventListener > &aListener) override
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
css::uno::WeakReference< css::lang::XComponent > m_xWrapper
virtual ~OLockListener() override
std::mutex m_aMutex
comphelper::OInterfaceContainerHelper4< css::lang::XEventListener > m_aListenersContainer
virtual void SAL_CALL queryClosing(const css::lang::EventObject &Source, sal_Bool GetsOwnership) override
AnyEventRef aEvent