diff options
Diffstat (limited to '')
-rw-r--r-- | cppuhelper/source/component_context.cxx | 590 |
1 files changed, 590 insertions, 0 deletions
diff --git a/cppuhelper/source/component_context.cxx b/cppuhelper/source/component_context.cxx new file mode 100644 index 000000000..81c7d306f --- /dev/null +++ b/cppuhelper/source/component_context.cxx @@ -0,0 +1,590 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <unordered_map> + +#include <osl/diagnose.h> +#include <osl/mutex.hxx> + +#include <sal/log.hxx> + +#include <uno/lbnames.h> +#include <uno/mapping.hxx> + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/component_context.hxx> +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/XSingleComponentFactory.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/uno/DeploymentException.hpp> +#include <com/sun/star/uno/RuntimeException.hpp> + +#include <comphelper/sequence.hxx> + +#include <memory> +#include <utility> + +constexpr OUStringLiteral SMGR_SINGLETON = u"/singletons/com.sun.star.lang.theServiceManager"; +constexpr OUStringLiteral TDMGR_SINGLETON = u"/singletons/com.sun.star.reflection.theTypeDescriptionManager"; +constexpr OUStringLiteral AC_SINGLETON = u"/singletons/com.sun.star.security.theAccessController"; + +using namespace ::osl; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; + +namespace cppu +{ + +static void try_dispose( Reference< XInterface > const & xInstance ) +{ + Reference< lang::XComponent > xComp( xInstance, UNO_QUERY ); + if (xComp.is()) + { + xComp->dispose(); + } +} + +static void try_dispose( Reference< lang::XComponent > const & xComp ) +{ + if (xComp.is()) + { + xComp->dispose(); + } +} + +namespace { + +class DisposingForwarder + : public WeakImplHelper< lang::XEventListener > +{ + Reference< lang::XComponent > m_xTarget; + + explicit DisposingForwarder( Reference< lang::XComponent > const & xTarget ) + : m_xTarget( xTarget ) + { + OSL_ASSERT( m_xTarget.is() ); + } +public: + // listens at source for disposing, then disposes target + static inline void listen( + Reference< lang::XComponent > const & xSource, + Reference< lang::XComponent > const & xTarget ); + + virtual void SAL_CALL disposing( lang::EventObject const & rSource ) override; +}; + +} + +inline void DisposingForwarder::listen( + Reference< lang::XComponent > const & xSource, + Reference< lang::XComponent > const & xTarget ) +{ + if (xSource.is()) + { + xSource->addEventListener( new DisposingForwarder( xTarget ) ); + } +} + +void DisposingForwarder::disposing( lang::EventObject const & ) +{ + m_xTarget->dispose(); + m_xTarget.clear(); +} + +namespace { + +class ComponentContext + : private cppu::BaseMutex + , public WeakComponentImplHelper< XComponentContext, + container::XNameContainer > +{ +protected: + Reference< XComponentContext > m_xDelegate; + + struct ContextEntry + { + Any value; + bool lateInit; + + ContextEntry( Any value_, bool lateInit_ ) + : value(std::move( value_ )) + , lateInit( lateInit_ ) + {} + }; + typedef std::unordered_map< OUString, ContextEntry > t_map; + t_map m_map; + + Reference< lang::XMultiComponentFactory > m_xSMgr; + +protected: + Any lookupMap( OUString const & rName ); + + virtual void SAL_CALL disposing() override; +public: + ComponentContext( + ContextEntry_Init const * pEntries, sal_Int32 nEntries, + Reference< XComponentContext > const & xDelegate ); + + // XComponentContext + virtual Any SAL_CALL getValueByName( OUString const & rName ) override; + virtual Reference<lang::XMultiComponentFactory> SAL_CALL getServiceManager() override; + + // XNameContainer + virtual void SAL_CALL insertByName( + OUString const & name, Any const & element ) override; + virtual void SAL_CALL removeByName( OUString const & name ) override; + // XNameReplace + virtual void SAL_CALL replaceByName( + OUString const & name, Any const & element ) override; + // XNameAccess + virtual Any SAL_CALL getByName( OUString const & name ) override; + virtual Sequence<OUString> SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName( OUString const & name ) override; + // XElementAccess + virtual Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; +}; + +} + +// XNameContainer + +void ComponentContext::insertByName( + OUString const & name, Any const & element ) +{ + ContextEntry entry( + element, + /* lateInit_: */ + name.startsWith( "/singletons/" ) && + !element.hasValue() ); + MutexGuard guard( m_aMutex ); + std::pair<t_map::iterator, bool> insertion( m_map.emplace( + name, entry ) ); + if (! insertion.second) + throw container::ElementExistException( + "element already exists: " + name, + static_cast<OWeakObject *>(this) ); +} + + +void ComponentContext::removeByName( OUString const & name ) +{ + MutexGuard guard( m_aMutex ); + t_map::iterator iFind( m_map.find( name ) ); + if (iFind == m_map.end()) + throw container::NoSuchElementException( + "no such element: " + name, + static_cast<OWeakObject *>(this) ); + + m_map.erase(iFind); +} + +// XNameReplace + +void ComponentContext::replaceByName( + OUString const & name, Any const & element ) +{ + MutexGuard guard( m_aMutex ); + t_map::iterator iFind( m_map.find( name ) ); + if (iFind == m_map.end()) + throw container::NoSuchElementException( + "no such element: " + name, + static_cast<OWeakObject *>(this) ); + if (name.startsWith( "/singletons/" ) && + !element.hasValue()) + { + iFind->second.value.clear(); + iFind->second.lateInit = true; + } + else + { + iFind->second.value = element; + iFind->second.lateInit = false; + } +} + +// XNameAccess + +Any ComponentContext::getByName( OUString const & name ) +{ + return getValueByName( name ); +} + + +Sequence<OUString> ComponentContext::getElementNames() +{ + MutexGuard guard( m_aMutex ); + return comphelper::mapKeysToSequence(m_map); +} + + +sal_Bool ComponentContext::hasByName( OUString const & name ) +{ + MutexGuard guard( m_aMutex ); + return m_map.find( name ) != m_map.end(); +} + +// XElementAccess + +Type ComponentContext::getElementType() +{ + return cppu::UnoType<void>::get(); +} + + +sal_Bool ComponentContext::hasElements() +{ + MutexGuard guard( m_aMutex ); + return ! m_map.empty(); +} + + +Any ComponentContext::lookupMap( OUString const & rName ) +{ + ResettableMutexGuard guard( m_aMutex ); + t_map::iterator iFind( m_map.find( rName ) ); + if (iFind == m_map.end()) + return Any(); + + ContextEntry& rFindEntry = iFind->second; + if (! rFindEntry.lateInit) + return rFindEntry.value; + + // late init singleton entry + Reference< XInterface > xInstance; + guard.clear(); + + try + { + Any usesService( getValueByName( rName + "/service" ) ); + Any args_( getValueByName( rName + "/arguments" ) ); + Sequence<Any> args; + if (args_.hasValue() && !(args_ >>= args)) + { + args = { args_ }; + } + + Reference< lang::XSingleComponentFactory > xFac; + if (usesService >>= xFac) // try via factory + { + xInstance = args.hasElements() + ? xFac->createInstanceWithArgumentsAndContext( args, this ) + : xFac->createInstanceWithContext( this ); + } + else + { + Reference< lang::XSingleServiceFactory > xFac2; + if (usesService >>= xFac2) + { + // try via old XSingleServiceFactory + xInstance = args.hasElements() + ? xFac2->createInstanceWithArguments( args ) + : xFac2->createInstance(); + } + else if (m_xSMgr.is()) // optionally service name + { + OUString serviceName; + if ((usesService >>= serviceName) && + !serviceName.isEmpty()) + { + xInstance = args.hasElements() + ? m_xSMgr->createInstanceWithArgumentsAndContext( + serviceName, args, this ) + : m_xSMgr->createInstanceWithContext( + serviceName, this ); + } + } + } + } + catch (const RuntimeException &) + { + throw; + } + catch (const Exception & exc) + { + SAL_WARN( + "cppuhelper", + "exception occurred raising singleton \"" << rName << "\": " + << exc); + } + + SAL_WARN_IF(!xInstance.is(), + "cppuhelper", "no service object raising singleton " << rName); + + Any ret; + guard.reset(); + iFind = m_map.find( rName ); + if (iFind != m_map.end()) + { + ContextEntry & rEntry = iFind->second; + if (rEntry.lateInit) + { + rEntry.value <<= xInstance; + rEntry.lateInit = false; + return rEntry.value; + } + ret = rEntry.value; + } + guard.clear(); + if (ret != xInstance) { + try_dispose( xInstance ); + } + return ret; +} + + +Any ComponentContext::getValueByName( OUString const & rName ) +{ + // to determine the root context: + if ( rName == "_root" ) + { + if (m_xDelegate.is()) + return m_xDelegate->getValueByName( rName ); + return Any( Reference<XComponentContext>(this) ); + } + + Any ret( lookupMap( rName ) ); + if (!ret.hasValue() && m_xDelegate.is()) + { + return m_xDelegate->getValueByName( rName ); + } + return ret; +} + +Reference< lang::XMultiComponentFactory > ComponentContext::getServiceManager() +{ + if ( !m_xSMgr.is() ) + { + throw DeploymentException( + "null component context service manager", + static_cast<OWeakObject *>(this) ); + } + return m_xSMgr; +} + +void ComponentContext::disposing() +{ + Reference< lang::XComponent > xTDMgr, xAC; // to be disposed separately + + // dispose all context objects + for ( auto& [rName, rEntry] : m_map ) + { + // service manager disposed separately + if (!m_xSMgr.is() || + !rName.startsWith( SMGR_SINGLETON )) + { + if (rEntry.lateInit) + { + // late init + MutexGuard guard( m_aMutex ); + if (rEntry.lateInit) + { + rEntry.value.clear(); // release factory + rEntry.lateInit = false; + continue; + } + } + + Reference< lang::XComponent > xComp; + rEntry.value >>= xComp; + if (xComp.is()) + { + if ( rName == TDMGR_SINGLETON ) + { + xTDMgr = xComp; + } + else if ( rName == AC_SINGLETON ) + { + xAC = xComp; + } + else // dispose immediately + { + xComp->dispose(); + } + } + } + } + + // dispose service manager + try_dispose( m_xSMgr ); + m_xSMgr.clear(); + // dispose ac + try_dispose( xAC ); + // dispose tdmgr; revokes callback from cppu runtime + try_dispose( xTDMgr ); + + m_map.clear(); + + // Hack to terminate any JNI bridge's AsynchronousFinalizer thread (as JNI + // proxies get finalized with arbitrary delay, so the bridge typically does + // not dispose itself early enough before the process exits): + uno_Environment ** envs; + sal_Int32 envCount; + uno_getRegisteredEnvironments( + &envs, &envCount, &rtl_allocateMemory, OUString("java").pData); + assert(envCount >= 0); + assert(envCount == 0 || envs != nullptr); + if (envs) { + for (sal_Int32 i = 0; i != envCount; ++i) { + assert(envs[i] != nullptr); + assert(envs[i]->dispose != nullptr); + (*envs[i]->dispose)(envs[i]); + } + std::free(envs); + } +} + +ComponentContext::ComponentContext( + ContextEntry_Init const * pEntries, sal_Int32 nEntries, + Reference< XComponentContext > const & xDelegate ) + : WeakComponentImplHelper( m_aMutex ), + m_xDelegate( xDelegate ) +{ + for ( sal_Int32 nPos = 0; nPos < nEntries; ++nPos ) + { + ContextEntry_Init const & rEntry = pEntries[ nPos ]; + + if ( rEntry.name == SMGR_SINGLETON ) + { + rEntry.value >>= m_xSMgr; + } + + if (rEntry.bLateInitService) + { + // singleton entry + m_map.emplace( rEntry.name, ContextEntry( Any(), true ) ); + // service + m_map.emplace( rEntry.name + "/service", ContextEntry( rEntry.value, false ) ); + // initial-arguments are provided as optional context entry + } + else + { + // only value, no late init factory nor string + m_map.emplace( rEntry.name, ContextEntry( rEntry.value, false ) ); + } + } + + if (m_xSMgr.is() || !m_xDelegate.is()) + return; + + // wrap delegate's smgr XPropertySet into new smgr + Reference< lang::XMultiComponentFactory > xMgr( m_xDelegate->getServiceManager() ); + if (!xMgr.is()) + return; + + osl_atomic_increment( &m_refCount ); + try + { + // create new smgr based on delegate's one + m_xSMgr.set( + xMgr->createInstanceWithContext( + "com.sun.star.comp.stoc.OServiceManagerWrapper", xDelegate ), + UNO_QUERY ); + // patch DefaultContext property of new one + Reference< beans::XPropertySet > xProps( m_xSMgr, UNO_QUERY ); + OSL_ASSERT( xProps.is() ); + if (xProps.is()) + { + Reference< XComponentContext > xThis( this ); + xProps->setPropertyValue( "DefaultContext", Any( xThis ) ); + } + } + catch (...) + { + osl_atomic_decrement( &m_refCount ); + throw; + } + osl_atomic_decrement( &m_refCount ); + OSL_ASSERT( m_xSMgr.is() ); +} + + +extern "C" { static void s_createComponentContext_v(va_list * pParam) +{ + ContextEntry_Init const * pEntries = va_arg(*pParam, ContextEntry_Init const *); + sal_Int32 nEntries = va_arg(*pParam, sal_Int32); + XComponentContext * pDelegatee = va_arg(*pParam, XComponentContext *); + void ** ppContext = va_arg(*pParam, void **); + uno::Mapping * pTarget2curr = va_arg(*pParam, uno::Mapping *); + + Reference<XComponentContext> xDelegate(pDelegatee, SAL_NO_ACQUIRE); + Reference<XComponentContext> xContext; + + if (nEntries > 0) + { + try + { + ComponentContext * p = new ComponentContext( pEntries, nEntries, xDelegate ); + xContext.set(p); + // listen delegate for disposing, to dispose this (wrapping) context first. + DisposingForwarder::listen( Reference< lang::XComponent >::query( xDelegate ), p ); + } + catch (Exception & exc) + { + SAL_WARN( "cppuhelper", exc ); + xContext.clear(); + } + } + else + { + xContext = xDelegate; + } + + *ppContext = pTarget2curr->mapInterface(xContext.get(), cppu::UnoType<decltype(xContext)>::get()); +}} + +Reference< XComponentContext > SAL_CALL createComponentContext( + ContextEntry_Init const * pEntries, sal_Int32 nEntries, + Reference< XComponentContext > const & xDelegate ) +{ + uno::Environment curr_env(Environment::getCurrent()); + uno::Environment source_env(CPPU_CURRENT_LANGUAGE_BINDING_NAME); + + uno::Mapping curr2source(curr_env, source_env); + uno::Mapping source2curr(source_env, curr_env); + + std::unique_ptr<ContextEntry_Init[]> mapped_entries(new ContextEntry_Init[nEntries]); + for (sal_Int32 nPos = 0; nPos < nEntries; ++ nPos) + { + mapped_entries[nPos].bLateInitService = pEntries[nPos].bLateInitService; + mapped_entries[nPos].name = pEntries[nPos].name; + + uno_type_any_constructAndConvert(&mapped_entries[nPos].value, + const_cast<void *>(pEntries[nPos].value.getValue()), + pEntries[nPos].value.getValueTypeRef(), + curr2source.get()); + } + + void * mapped_delegate = curr2source.mapInterface(xDelegate.get(), cppu::UnoType<decltype(xDelegate)>::get()); + XComponentContext * pXComponentContext = nullptr; + source_env.invoke(s_createComponentContext_v, mapped_entries.get(), nEntries, mapped_delegate, &pXComponentContext, &source2curr); + mapped_entries.reset(); + + return Reference<XComponentContext>(pXComponentContext, SAL_NO_ACQUIRE); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |