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