LibreOffice Module cppuhelper (master)  1
component_context.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 #include <unordered_map>
21 
22 #include <osl/diagnose.h>
23 #include <osl/mutex.hxx>
24 
25 #include <sal/log.hxx>
26 
27 #include <uno/lbnames.h>
28 #include <uno/mapping.hxx>
29 
30 #include <cppuhelper/compbase.hxx>
32 #include <cppuhelper/implbase.hxx>
33 
34 #include <com/sun/star/container/XNameContainer.hpp>
35 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
36 #include <com/sun/star/lang/XSingleComponentFactory.hpp>
37 #include <com/sun/star/lang/XMultiComponentFactory.hpp>
38 #include <com/sun/star/lang/XComponent.hpp>
39 #include <com/sun/star/beans/XPropertySet.hpp>
40 #include <com/sun/star/uno/DeploymentException.hpp>
41 #include <com/sun/star/uno/RuntimeException.hpp>
42 
43 #include <comphelper/sequence.hxx>
44 
45 #include <memory>
46 
47 #define SMGR_SINGLETON "/singletons/com.sun.star.lang.theServiceManager"
48 #define TDMGR_SINGLETON "/singletons/com.sun.star.reflection.theTypeDescriptionManager"
49 #define AC_SINGLETON "/singletons/com.sun.star.security.theAccessController"
50 
51 using namespace ::osl;
52 using namespace ::com::sun::star::uno;
53 using namespace ::com::sun::star;
54 
55 namespace cppu
56 {
57 
58 static void try_dispose( Reference< XInterface > const & xInstance )
59 {
60  Reference< lang::XComponent > xComp( xInstance, UNO_QUERY );
61  if (xComp.is())
62  {
63  xComp->dispose();
64  }
65 }
66 
67 static void try_dispose( Reference< lang::XComponent > const & xComp )
68 {
69  if (xComp.is())
70  {
71  xComp->dispose();
72  }
73 }
74 
75 namespace {
76 
77 class DisposingForwarder
78  : public WeakImplHelper< lang::XEventListener >
79 {
81 
82  explicit DisposingForwarder( Reference< lang::XComponent > const & xTarget )
83  : m_xTarget( xTarget )
84  {
85  OSL_ASSERT( m_xTarget.is() );
86  }
87 public:
88  // listens at source for disposing, then disposes target
89  static inline void listen(
90  Reference< lang::XComponent > const & xSource,
91  Reference< lang::XComponent > const & xTarget );
92 
93  virtual void SAL_CALL disposing( lang::EventObject const & rSource ) override;
94 };
95 
96 }
97 
98 inline void DisposingForwarder::listen(
99  Reference< lang::XComponent > const & xSource,
100  Reference< lang::XComponent > const & xTarget )
101 {
102  if (xSource.is())
103  {
104  xSource->addEventListener( new DisposingForwarder( xTarget ) );
105  }
106 }
107 
108 void DisposingForwarder::disposing( lang::EventObject const & )
109 {
110  m_xTarget->dispose();
111  m_xTarget.clear();
112 }
113 
114 namespace {
115 
116 struct MutexHolder
117 {
118 protected:
119  Mutex m_mutex;
120 };
121 
122 
123 class ComponentContext
124  : private MutexHolder
125  , public WeakComponentImplHelper< XComponentContext,
126  container::XNameContainer >
127 {
128 protected:
130 
131  struct ContextEntry
132  {
134  bool lateInit;
135 
136  ContextEntry( Any const & value_, bool lateInit_ )
137  : value( value_ )
138  , lateInit( lateInit_ )
139  {}
140  };
141  typedef std::unordered_map< OUString, ContextEntry > t_map;
142  t_map m_map;
143 
144  Reference< lang::XMultiComponentFactory > m_xSMgr;
145 
146 protected:
147  Any lookupMap( OUString const & rName );
148 
149  virtual void SAL_CALL disposing() override;
150 public:
151  ComponentContext(
152  ContextEntry_Init const * pEntries, sal_Int32 nEntries,
153  Reference< XComponentContext > const & xDelegate );
154 
155  // XComponentContext
156  virtual Any SAL_CALL getValueByName( OUString const & rName ) override;
157  virtual Reference<lang::XMultiComponentFactory> SAL_CALL getServiceManager() override;
158 
159  // XNameContainer
160  virtual void SAL_CALL insertByName(
161  OUString const & name, Any const & element ) override;
162  virtual void SAL_CALL removeByName( OUString const & name ) override;
163  // XNameReplace
164  virtual void SAL_CALL replaceByName(
165  OUString const & name, Any const & element ) override;
166  // XNameAccess
167  virtual Any SAL_CALL getByName( OUString const & name ) override;
168  virtual Sequence<OUString> SAL_CALL getElementNames() override;
169  virtual sal_Bool SAL_CALL hasByName( OUString const & name ) override;
170  // XElementAccess
171  virtual Type SAL_CALL getElementType() override;
172  virtual sal_Bool SAL_CALL hasElements() override;
173 };
174 
175 }
176 
177 // XNameContainer
178 
179 void ComponentContext::insertByName(
180  OUString const & name, Any const & element )
181 {
182  ContextEntry entry(
183  element,
184  /* lateInit_: */
185  name.startsWith( "/singletons/" ) &&
186  !element.hasValue() );
187  MutexGuard guard( m_mutex );
188  std::pair<t_map::iterator, bool> insertion( m_map.emplace(
189  name, entry ) );
190  if (! insertion.second)
191  throw container::ElementExistException(
192  "element already exists: " + name,
193  static_cast<OWeakObject *>(this) );
194 }
195 
196 
197 void ComponentContext::removeByName( OUString const & name )
198 {
199  MutexGuard guard( m_mutex );
200  t_map::iterator iFind( m_map.find( name ) );
201  if (iFind == m_map.end())
202  throw container::NoSuchElementException(
203  "no such element: " + name,
204  static_cast<OWeakObject *>(this) );
205 
206  m_map.erase(iFind);
207 }
208 
209 // XNameReplace
210 
211 void ComponentContext::replaceByName(
212  OUString const & name, Any const & element )
213 {
214  MutexGuard guard( m_mutex );
215  t_map::iterator iFind( m_map.find( name ) );
216  if (iFind == m_map.end())
217  throw container::NoSuchElementException(
218  "no such element: " + name,
219  static_cast<OWeakObject *>(this) );
220  if (name.startsWith( "/singletons/" ) &&
221  !element.hasValue())
222  {
223  iFind->second.value.clear();
224  iFind->second.lateInit = true;
225  }
226  else
227  {
228  iFind->second.value = element;
229  iFind->second.lateInit = false;
230  }
231 }
232 
233 // XNameAccess
234 
235 Any ComponentContext::getByName( OUString const & name )
236 {
237  return getValueByName( name );
238 }
239 
240 
241 Sequence<OUString> ComponentContext::getElementNames()
242 {
243  MutexGuard guard( m_mutex );
245 }
246 
247 
248 sal_Bool ComponentContext::hasByName( OUString const & name )
249 {
250  MutexGuard guard( m_mutex );
251  return m_map.find( name ) != m_map.end();
252 }
253 
254 // XElementAccess
255 
256 Type ComponentContext::getElementType()
257 {
258  return cppu::UnoType<void>::get();
259 }
260 
261 
262 sal_Bool ComponentContext::hasElements()
263 {
264  MutexGuard guard( m_mutex );
265  return ! m_map.empty();
266 }
267 
268 
269 Any ComponentContext::lookupMap( OUString const & rName )
270 {
271  ResettableMutexGuard guard( m_mutex );
272  t_map::iterator iFind( m_map.find( rName ) );
273  if (iFind == m_map.end())
274  return Any();
275 
276  ContextEntry& rFindEntry = iFind->second;
277  if (! rFindEntry.lateInit)
278  return rFindEntry.value;
279 
280  // late init singleton entry
281  Reference< XInterface > xInstance;
282  guard.clear();
283 
284  try
285  {
286  Any usesService( getValueByName( rName + "/service" ) );
287  Any args_( getValueByName( rName + "/arguments" ) );
288  Sequence<Any> args;
289  if (args_.hasValue() && !(args_ >>= args))
290  {
291  args.realloc( 1 );
292  args[ 0 ] = args_;
293  }
294 
295  Reference< lang::XSingleComponentFactory > xFac;
296  if (usesService >>= xFac) // try via factory
297  {
298  xInstance = args.hasElements()
299  ? xFac->createInstanceWithArgumentsAndContext( args, this )
300  : xFac->createInstanceWithContext( this );
301  }
302  else
303  {
304  Reference< lang::XSingleServiceFactory > xFac2;
305  if (usesService >>= xFac2)
306  {
307  // try via old XSingleServiceFactory
308  xInstance = args.hasElements()
309  ? xFac2->createInstanceWithArguments( args )
310  : xFac2->createInstance();
311  }
312  else if (m_xSMgr.is()) // optionally service name
313  {
314  OUString serviceName;
315  if ((usesService >>= serviceName) &&
316  !serviceName.isEmpty())
317  {
318  xInstance = args.hasElements()
319  ? m_xSMgr->createInstanceWithArgumentsAndContext(
320  serviceName, args, this )
321  : m_xSMgr->createInstanceWithContext(
322  serviceName, this );
323  }
324  }
325  }
326  }
327  catch (const RuntimeException &)
328  {
329  throw;
330  }
331  catch (const Exception & exc)
332  {
333  SAL_WARN(
334  "cppuhelper",
335  "exception occurred raising singleton \"" << rName << "\": "
336  << exc);
337  }
338 
339  SAL_WARN_IF(!xInstance.is(),
340  "cppuhelper", "no service object raising singleton " << rName);
341 
342  Any ret;
343  guard.reset();
344  iFind = m_map.find( rName );
345  if (iFind != m_map.end())
346  {
347  ContextEntry & rEntry = iFind->second;
348  if (rEntry.lateInit)
349  {
350  rEntry.value <<= xInstance;
351  rEntry.lateInit = false;
352  return rEntry.value;
353  }
354  ret = rEntry.value;
355  }
356  guard.clear();
357  if (ret != xInstance) {
358  try_dispose( xInstance );
359  }
360  return ret;
361 }
362 
363 
364 Any ComponentContext::getValueByName( OUString const & rName )
365 {
366  // to determine the root context:
367  if ( rName == "_root" )
368  {
369  if (m_xDelegate.is())
370  return m_xDelegate->getValueByName( rName );
371  return Any( Reference<XComponentContext>(this) );
372  }
373 
374  Any ret( lookupMap( rName ) );
375  if (!ret.hasValue() && m_xDelegate.is())
376  {
377  return m_xDelegate->getValueByName( rName );
378  }
379  return ret;
380 }
381 
382 Reference< lang::XMultiComponentFactory > ComponentContext::getServiceManager()
383 {
384  if ( !m_xSMgr.is() )
385  {
386  throw DeploymentException(
387  "null component context service manager",
388  static_cast<OWeakObject *>(this) );
389  }
390  return m_xSMgr;
391 }
392 
393 void ComponentContext::disposing()
394 {
395  Reference< lang::XComponent > xTDMgr, xAC; // to be disposed separately
396 
397  // dispose all context objects
398  for ( auto& [rName, rEntry] : m_map )
399  {
400  // service manager disposed separately
401  if (!m_xSMgr.is() ||
402  !rName.startsWith( SMGR_SINGLETON ))
403  {
404  if (rEntry.lateInit)
405  {
406  // late init
407  MutexGuard guard( m_mutex );
408  if (rEntry.lateInit)
409  {
410  rEntry.value.clear(); // release factory
411  rEntry.lateInit = false;
412  continue;
413  }
414  }
415 
417  rEntry.value >>= xComp;
418  if (xComp.is())
419  {
420  if ( rName == TDMGR_SINGLETON )
421  {
422  xTDMgr = xComp;
423  }
424  else if ( rName == AC_SINGLETON )
425  {
426  xAC = xComp;
427  }
428  else // dispose immediately
429  {
430  xComp->dispose();
431  }
432  }
433  }
434  }
435 
436  // dispose service manager
437  try_dispose( m_xSMgr );
438  m_xSMgr.clear();
439  // dispose ac
440  try_dispose( xAC );
441  // dispose tdmgr; revokes callback from cppu runtime
442  try_dispose( xTDMgr );
443 
444  m_map.clear();
445 
446  // Hack to terminate any JNI bridge's AsynchronousFinalizer thread (as JNI
447  // proxies get finalized with arbitrary delay, so the bridge typically does
448  // not dispose itself early enough before the process exits):
449  uno_Environment ** envs;
450  sal_Int32 envCount;
452  &envs, &envCount, &rtl_allocateMemory, OUString("java").pData);
453  assert(envCount >= 0);
454  assert(envCount == 0 || envs != nullptr);
455  if (envs) {
456  for (sal_Int32 i = 0; i != envCount; ++i) {
457  assert(envs[i] != nullptr);
458  assert(envs[i]->dispose != nullptr);
459  (*envs[i]->dispose)(envs[i]);
460  }
461  std::free(envs);
462  }
463 }
464 
465 ComponentContext::ComponentContext(
466  ContextEntry_Init const * pEntries, sal_Int32 nEntries,
467  Reference< XComponentContext > const & xDelegate )
468  : WeakComponentImplHelper( m_mutex ),
469  m_xDelegate( xDelegate )
470 {
471  for ( sal_Int32 nPos = 0; nPos < nEntries; ++nPos )
472  {
473  ContextEntry_Init const & rEntry = pEntries[ nPos ];
474 
475  if ( rEntry.name == SMGR_SINGLETON )
476  {
477  rEntry.value >>= m_xSMgr;
478  }
479 
480  if (rEntry.bLateInitService)
481  {
482  // singleton entry
483  m_map.emplace( rEntry.name, ContextEntry( Any(), true ) );
484  // service
485  m_map.emplace( rEntry.name + "/service", ContextEntry( rEntry.value, false ) );
486  // initial-arguments are provided as optional context entry
487  }
488  else
489  {
490  // only value, no late init factory nor string
491  m_map.emplace( rEntry.name, ContextEntry( rEntry.value, false ) );
492  }
493  }
494 
495  if (m_xSMgr.is() || !m_xDelegate.is())
496  return;
497 
498  // wrap delegate's smgr XPropertySet into new smgr
499  Reference< lang::XMultiComponentFactory > xMgr( m_xDelegate->getServiceManager() );
500  if (!xMgr.is())
501  return;
502 
503  osl_atomic_increment( &m_refCount );
504  try
505  {
506  // create new smgr based on delegate's one
507  m_xSMgr.set(
508  xMgr->createInstanceWithContext(
509  "com.sun.star.comp.stoc.OServiceManagerWrapper", xDelegate ),
510  UNO_QUERY );
511  // patch DefaultContext property of new one
512  Reference< beans::XPropertySet > xProps( m_xSMgr, UNO_QUERY );
513  OSL_ASSERT( xProps.is() );
514  if (xProps.is())
515  {
516  Reference< XComponentContext > xThis( this );
517  xProps->setPropertyValue( "DefaultContext", Any( xThis ) );
518  }
519  }
520  catch (...)
521  {
522  osl_atomic_decrement( &m_refCount );
523  throw;
524  }
525  osl_atomic_decrement( &m_refCount );
526  OSL_ASSERT( m_xSMgr.is() );
527 }
528 
529 
530 extern "C" { static void s_createComponentContext_v(va_list * pParam)
531 {
532  ContextEntry_Init const * pEntries = va_arg(*pParam, ContextEntry_Init const *);
533  sal_Int32 nEntries = va_arg(*pParam, sal_Int32);
534  XComponentContext * pDelegatee = va_arg(*pParam, XComponentContext *);
535  void ** ppContext = va_arg(*pParam, void **);
536  uno::Mapping * pTarget2curr = va_arg(*pParam, uno::Mapping *);
537 
538  Reference<XComponentContext> xDelegate(pDelegatee, SAL_NO_ACQUIRE);
540 
541  if (nEntries > 0)
542  {
543  try
544  {
545  ComponentContext * p = new ComponentContext( pEntries, nEntries, xDelegate );
546  xContext.set(p);
547  // listen delegate for disposing, to dispose this (wrapping) context first.
548  DisposingForwarder::listen( Reference< lang::XComponent >::query( xDelegate ), p );
549  }
550  catch (Exception & exc)
551  {
552  SAL_WARN( "cppuhelper", exc );
553  xContext.clear();
554  }
555  }
556  else
557  {
558  xContext = xDelegate;
559  }
560 
561  *ppContext = pTarget2curr->mapInterface(xContext.get(), cppu::UnoType<decltype(xContext)>::get());
562 }}
563 
565  ContextEntry_Init const * pEntries, sal_Int32 nEntries,
566  Reference< XComponentContext > const & xDelegate )
567 {
568  uno::Environment curr_env(Environment::getCurrent());
569  uno::Environment source_env(CPPU_CURRENT_LANGUAGE_BINDING_NAME);
570 
571  uno::Mapping curr2source(curr_env, source_env);
572  uno::Mapping source2curr(source_env, curr_env);
573 
574  std::unique_ptr<ContextEntry_Init[]> mapped_entries(new ContextEntry_Init[nEntries]);
575  for (sal_Int32 nPos = 0; nPos < nEntries; ++ nPos)
576  {
577  mapped_entries[nPos].bLateInitService = pEntries[nPos].bLateInitService;
578  mapped_entries[nPos].name = pEntries[nPos].name;
579 
581  const_cast<void *>(pEntries[nPos].value.getValue()),
582  pEntries[nPos].value.getValueTypeRef(),
583  curr2source.get());
584  }
585 
586  void * mapped_delegate = curr2source.mapInterface(xDelegate.get(), cppu::UnoType<decltype(xDelegate)>::get());
587  XComponentContext * pXComponentContext = nullptr;
588  source_env.invoke(s_createComponentContext_v, mapped_entries.get(), nEntries, mapped_delegate, &pXComponentContext, &source2curr);
589  mapped_entries.reset();
590 
591  return Reference<XComponentContext>(pXComponentContext, SAL_NO_ACQUIRE);
592 }
593 
594 }
595 
596 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Type
bool bLateInitService
late init denotes an object that will be raised when first get() is calling for it ...
void SAL_CALL uno_getRegisteredEnvironments(uno_Environment ***pppEnvs, sal_Int32 *pnLen, uno_memAlloc memAlloc, rtl_uString *pEnvDcp) SAL_THROW_EXTERN_C()
struct _uno_Environment uno_Environment
Definition: factory.hxx:45
Reference< XComponentContext > SAL_CALL createComponentContext(ContextEntry_Init const *pEntries, sal_Int32 nEntries, Reference< XComponentContext > const &xDelegate)
#define AC_SINGLETON
tuple args
bool lateInit
Reference< XComponentContext > m_xDelegate
void SAL_CALL uno_type_any_constructAndConvert(uno_Any *pDest, void *pSource, typelib_TypeDescriptionReference *pType, uno_Mapping *mapping) SAL_THROW_EXTERN_C()
t_map m_map
static void try_dispose(Reference< XInterface > const &xInstance)
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
int i
::rtl::OUString name
name of context value
unsigned char sal_Bool
static void s_createComponentContext_v(va_list *pParam)
css::uno::Type const & get()
Reference< lang::XMultiComponentFactory > m_xSMgr
Mutex m_mutex
#define SAL_WARN_IF(condition, area, stream)
void * p
Reference< lang::XComponent > m_xTarget
Any value
#define SMGR_SINGLETON
#define SAL_WARN(area, stream)
#define TDMGR_SINGLETON
sal_uInt16 nPos
css::uno::Sequence< typename M::key_type > mapKeysToSequence(M const &map)
Context entries init struct calling createComponentContext().