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