diff options
author | Stephan Bergmann <sbergman@redhat.com> | 2012-06-01 09:59:49 +0200 |
---|---|---|
committer | Stephan Bergmann <sbergman@redhat.com> | 2012-06-01 09:59:49 +0200 |
commit | c16d0dd846e25df56feffe868a15eb33bb55e42a (patch) | |
tree | f9190a6ce0e43007db2f3a328020160de3f577ee | |
parent | 03fc584fda14a0d94b5034cf2bc6f215a50a1212 (diff) |
fdo#49291 Postpone actual factory instantiation as long as possible
...see comment in ServiceManager::createContentEnumeration for a rationale.
Splitting ImplementationInfo out of Implementation has become necessary to avoid
circular references.
Change-Id: I29aef81ce78b9ab71e18663f8c7e6ca913c6a650
-rw-r--r-- | cppuhelper/source/defaultbootstrap.cxx | 433 |
1 files changed, 314 insertions, 119 deletions
diff --git a/cppuhelper/source/defaultbootstrap.cxx b/cppuhelper/source/defaultbootstrap.cxx index c903c2bae08c..55a4f098e916 100644 --- a/cppuhelper/source/defaultbootstrap.cxx +++ b/cppuhelper/source/defaultbootstrap.cxx @@ -60,6 +60,7 @@ #include "cppuhelper/compbase8.hxx" #include "cppuhelper/component_context.hxx" #include "cppuhelper/implbase1.hxx" +#include "cppuhelper/implbase3.hxx" #include "cppuhelper/shlib.hxx" #include "osl/file.hxx" #include "registry/registry.hxx" @@ -124,36 +125,51 @@ void decodeRdbUri(rtl::OUString * uri, bool * optional, bool * directory) { } } +struct ImplementationInfo: private boost::noncopyable { + ImplementationInfo( + rtl::OUString const & theName, rtl::OUString const & theLoader, + rtl::OUString const & theUri, rtl::OUString const & thePrefix, + css::uno::Reference< css::uno::XComponentContext > const & + theAlienContext): + name(theName), loader(theLoader), uri(theUri), prefix(thePrefix), + alienContext(theAlienContext) + {} + + explicit ImplementationInfo(rtl::OUString const & theName): name(theName) {} + + rtl::OUString const name; + rtl::OUString const loader; + rtl::OUString const uri; + rtl::OUString const prefix; + css::uno::Reference< css::uno::XComponentContext > const alienContext; + std::vector< rtl::OUString > services; + std::vector< rtl::OUString > singletons; +}; + struct Implementation: private boost::noncopyable { Implementation( - rtl::OUString const & theName, rtl::OUString const & theLoader, - rtl::OUString const & theUri, - rtl::OUString const & thePrefix = rtl::OUString(), + rtl::OUString const & name, rtl::OUString const & loader, + rtl::OUString const & uri, + rtl::OUString const & prefix = rtl::OUString(), css::uno::Reference< css::uno::XComponentContext > const & - theAlienContext + alienContext = css::uno::Reference< css::uno::XComponentContext >()): - name(theName), loader(theLoader), uri(theUri), prefix(thePrefix), - alienContext(theAlienContext), loaded(false) + info(new ImplementationInfo(name, loader, uri, prefix, alienContext)), + loaded(false) {} Implementation( - rtl::OUString const & theName, + rtl::OUString const & name, css::uno::Reference< css::lang::XSingleComponentFactory > const & theFactory1, css::uno::Reference< css::lang::XSingleServiceFactory > const & theFactory2, css::uno::Reference< css::lang::XComponent > const & theComponent): - name(theName), factory1(theFactory1), factory2(theFactory2), - component(theComponent), loaded(true) + info(new ImplementationInfo(name)), factory1(theFactory1), + factory2(theFactory2), component(theComponent), loaded(true) {} - rtl::OUString const name; - rtl::OUString const loader; - rtl::OUString const uri; - rtl::OUString const prefix; - css::uno::Reference< css::uno::XComponentContext > const alienContext; - std::vector< rtl::OUString > services; - std::vector< rtl::OUString > singletons; + boost::shared_ptr< ImplementationInfo > info; css::uno::Reference< css::lang::XSingleComponentFactory > factory1; css::uno::Reference< css::lang::XSingleServiceFactory > factory2; css::uno::Reference< css::lang::XComponent > component; @@ -463,13 +479,13 @@ void Parser::handleImplementation() { void Parser::handleService() { rtl::OUString name(getNameAttribute()); - implementation_->services.push_back(name); + implementation_->info->services.push_back(name); data_->services[name].push_back(implementation_); } void Parser::handleSingleton() { rtl::OUString name(getNameAttribute()); - implementation_->singletons.push_back(name); + implementation_->info->singletons.push_back(name); data_->singletons[name].push_back(implementation_); } @@ -585,8 +601,18 @@ public: context_ = context; } + css::uno::Reference< css::uno::XComponentContext > getContext() const { + assert(context_.is()); + return context_; + } + Data const & getData() const { return data_; } + void loadImplementation( + boost::shared_ptr< ImplementationInfo > const & info, + css::uno::Reference< css::lang::XSingleComponentFactory > * factory1, + css::uno::Reference< css::lang::XSingleServiceFactory > * factory2); + virtual rtl::OUString SAL_CALL getImplementationName() throw (css::uno::RuntimeException); @@ -757,12 +783,136 @@ private: boost::shared_ptr< Implementation > findServiceImplementation( rtl::OUString const & specifier); - void loadImplementation(Implementation * implementation); - css::uno::Reference< css::uno::XComponentContext > context_; Data data_; }; +class FactoryWrapper: + public cppu::WeakImplHelper3< + css::lang::XSingleComponentFactory, css::lang::XSingleServiceFactory, + css::lang::XServiceInfo >, + private boost::noncopyable +{ +public: + FactoryWrapper( + rtl::Reference< ServiceManager > const & manager, + boost::shared_ptr< ImplementationInfo > const & info): + manager_(manager), info_(info), loaded_(false) + { assert(manager.is() && info.get() != 0); } + +private: + virtual ~FactoryWrapper() {} + + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL + createInstanceWithContext( + css::uno::Reference< css::uno::XComponentContext > const & Context) + throw (css::uno::Exception, css::uno::RuntimeException); + + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL + createInstanceWithArgumentsAndContext( + css::uno::Sequence< css::uno::Any > const & Arguments, + css::uno::Reference< css::uno::XComponentContext > const & Context) + throw (css::uno::Exception, css::uno::RuntimeException); + + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL + createInstance() throw (css::uno::Exception, css::uno::RuntimeException); + + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL + createInstanceWithArguments( + css::uno::Sequence< css::uno::Any > const & Arguments) + throw (css::uno::Exception, css::uno::RuntimeException); + + virtual rtl::OUString SAL_CALL getImplementationName() + throw (css::uno::RuntimeException); + + virtual sal_Bool SAL_CALL supportsService(rtl::OUString const & ServiceName) + throw (css::uno::RuntimeException); + + virtual css::uno::Sequence< rtl::OUString > SAL_CALL + getSupportedServiceNames() throw (css::uno::RuntimeException); + + void loadImplementation(); + + rtl::Reference< ServiceManager > manager_; + boost::shared_ptr< ImplementationInfo > info_; + + osl::Mutex mutex_; + bool loaded_; + css::uno::Reference< css::lang::XSingleComponentFactory > factory1_; + css::uno::Reference< css::lang::XSingleServiceFactory > factory2_; +}; + +void ServiceManager::loadImplementation( + boost::shared_ptr< ImplementationInfo > const & info, + css::uno::Reference< css::lang::XSingleComponentFactory > * factory1, + css::uno::Reference< css::lang::XSingleServiceFactory > * factory2) +{ + assert( + info.get() != 0 && factory1 != 0 && !factory1->is() && factory2 != 0 + && !factory2->is()); + rtl::OUString uri; + try { + uri = cppu::bootstrap_expandUri(info->uri); + } catch (css::lang::IllegalArgumentException & e) { + throw css::uno::DeploymentException( + "Cannot expand URI" + info->uri + ": " + e.Message, + static_cast< cppu::OWeakObject * >(this)); + } + css::uno::Reference< css::uno::XInterface > f0; + // Shortcut loading via SharedLibrary loader, to pass in prefix argument + // (which the loader's activate implementation would normally obtain through + // the legacy xKey argument): + if (!info->alienContext.is() + && info->loader == "com.sun.star.loader.SharedLibrary") + { + rtl::OUString prefix(info->prefix); + if (!prefix.isEmpty()) { + prefix += "_"; + } + try { + f0 = cppu::loadSharedLibComponentFactory( + uri, rtl::OUString(), info->name, this, + css::uno::Reference< css::registry::XRegistryKey >(), prefix); + } catch (css::loader::CannotActivateFactoryException & e) { + throw css::uno::DeploymentException( + "Cannot activate implementation " + uri + ": " + e.Message, + static_cast< cppu::OWeakObject * >(this)); + } + } else { + SAL_INFO_IF( + !info->prefix.isEmpty(), "cppuhelper", + "Loader " << info->loader << " and non-empty prefix " + << info->prefix); + css::uno::Reference< css::uno::XComponentContext > ctxt; + css::uno::Reference< css::lang::XMultiComponentFactory > smgr; + if (info->alienContext.is()) { + ctxt = info->alienContext; + smgr = css::uno::Reference< css::lang::XMultiComponentFactory >( + ctxt->getServiceManager(), css::uno::UNO_SET_THROW); + } else { + assert(context_.is()); + ctxt = context_; + smgr = this; + } + css::uno::Reference< css::loader::XImplementationLoader > loader( + smgr->createInstanceWithContext(info->loader, ctxt), + css::uno::UNO_QUERY_THROW); + try { + f0 = loader->activate( + info->name, rtl::OUString(), uri, + css::uno::Reference< css::registry::XRegistryKey >()); + } catch (css::loader::CannotActivateFactoryException & e) { + throw css::uno::DeploymentException( + "Cannot activate implementation " + uri + ": " + e.Message, + static_cast< cppu::OWeakObject * >(this)); + } + } + factory1->set(f0, css::uno::UNO_QUERY); + if (!factory1->is()) { + factory2->set(f0, css::uno::UNO_QUERY); + } +} + rtl::OUString ServiceManager::getImplementationName() throw (css::uno::RuntimeException) { @@ -852,7 +1002,7 @@ ServiceManager::createInstanceWithContext( return impl->factory2->createInstance(); } throw css::uno::DeploymentException( - "Implementation " + impl->name + " does not provide a factory", + "Implementation " + impl->info->name + " does not provide a factory", static_cast< cppu::OWeakObject * >(this)); } @@ -876,7 +1026,7 @@ ServiceManager::createInstanceWithArgumentsAndContext( return impl->factory2->createInstanceWithArguments(Arguments); } throw css::uno::DeploymentException( - "Implementation " + impl->name + " does not provide a factory", + "Implementation " + impl->info->name + " does not provide a factory", static_cast< cppu::OWeakObject * >(this)); } @@ -1036,21 +1186,21 @@ ServiceManager::createContentEnumeration(rtl::OUString const & aServiceName) { Implementation * impl = i->get(); assert(impl != 0); - bool loaded; { osl::MutexGuard g(rBHelper.rMutex); if (isDisposed()) { factories.clear(); break; } - loaded = impl->loaded; - } - //TODO: There is a race here, as the relevant service factory can be - // removed while the mutex is unlocked and loading can thus fail, as the - // entity from which to load can disappear once the service factory is - // removed. - if (!loaded) { - loadImplementation(impl); + if (!impl->loaded) { + // Postpone actual factory instantiation as long as possible (so + // that e.g. opening LO's "Tools - Macros" menu does not try to + // instantiate a JVM, which can lead to a synchronous error + // dialog when no JVM is specified, and showing the dialog while + // hovering over a menu can cause trouble): + impl->factory1 = new FactoryWrapper(this, impl->info); + impl->loaded = true; + } } if (impl->factory1.is()) { factories.push_back(css::uno::makeAny(impl->factory1)); @@ -1058,7 +1208,8 @@ ServiceManager::createContentEnumeration(rtl::OUString const & aServiceName) factories.push_back(css::uno::makeAny(impl->factory2)); } else { throw css::uno::DeploymentException( - "Implementation " + impl->name + " does not provide a factory", + ("Implementation " + impl->info->name + + " does not provide a factory"), static_cast< cppu::OWeakObject * >(this)); } } @@ -1365,17 +1516,19 @@ bool ServiceManager::readLegacyRdbFile(rtl::OUString const & uri) { uri + ": duplicate <implementation name=\"" + name + "\">", css::uno::Reference< css::uno::XInterface >()); } - readLegacyRdbStrings(uri, implKey, "UNO/SERVICES", &impl->services); + readLegacyRdbStrings( + uri, implKey, "UNO/SERVICES", &impl->info->services); for (std::vector< rtl::OUString >::const_iterator j( - impl->services.begin()); - j != impl->services.end(); ++j) + impl->info->services.begin()); + j != impl->info->services.end(); ++j) { data_.services[*j].push_back(impl); } - readLegacyRdbStrings(uri, implKey, "UNO/SINGLETONS", &impl->singletons); + readLegacyRdbStrings( + uri, implKey, "UNO/SINGLETONS", &impl->info->singletons); for (std::vector< rtl::OUString >::const_iterator j( - impl->singletons.begin()); - j != impl->singletons.end(); ++j) + impl->info->singletons.begin()); + j != impl->info->singletons.end(); ++j) { data_.singletons[*j].push_back(impl); } @@ -1494,7 +1647,7 @@ void ServiceManager::insertLegacyFactory( css::uno::Sequence< rtl::OUString > services( factoryInfo->getSupportedServiceNames()); for (sal_Int32 i = 0; i != services.getLength(); ++i) { - impl->services.push_back(services[i]); + impl->info->services.push_back(services[i]); extra.services[services[i]].push_back(impl); } if (insertExtraData(extra) && comp.is()) { @@ -1560,15 +1713,15 @@ bool ServiceManager::insertExtraData(Data const & extra) { assert(i->second[0].get() != 0); SAL_INFO_IF( i->second.size() > 1, "cppuhelper", - "Arbitrarily chosing " << i->second[0]->name + "Arbitrarily chosing " << i->second[0]->info->name << " among multiple implementations for singleton " << i->first); try { cont->insertByName( - name + "/service", css::uno::Any(i->second[0]->name)); + name + "/service", css::uno::Any(i->second[0]->info->name)); } catch (css::container::ElementExistException &) { cont->replaceByName( - name + "/service", css::uno::Any(i->second[0]->name)); + name + "/service", css::uno::Any(i->second[0]->info->name)); } try { cont->insertByName(name, css::uno::Any()); @@ -1594,13 +1747,13 @@ void ServiceManager::removeRdbFiles(std::vector< rtl::OUString > const & uris) { j != data_.namedImplementations.end();) { assert(j->second.get() != 0); - if (j->second->uri == *i) { + if (j->second->info->uri == *i) { //TODO: The below leaves data_ in an inconsistent state upon // exceptions: removeFromImplementationMap( - &data_.services, j->second->services, j->second); + &data_.services, j->second->info->services, j->second); removeFromImplementationMap( - &data_.singletons, j->second->singletons, j->second); + &data_.singletons, j->second->info->singletons, j->second); data_.namedImplementations.erase(j++); } else { ++j; @@ -1626,11 +1779,11 @@ bool ServiceManager::removeLegacyFactory( assert(i->second.get() != 0); //TODO: The below leaves data_ in an inconsistent state upon exceptions: removeFromImplementationMap( - &data_.services, i->second->services, i->second); + &data_.services, i->second->info->services, i->second); removeFromImplementationMap( - &data_.singletons, i->second->singletons, i->second); - if (!i->second->name.isEmpty()) { - data_.namedImplementations.erase(i->second->name); + &data_.singletons, i->second->info->singletons, i->second); + if (!i->second->info->name.isEmpty()) { + data_.namedImplementations.erase(i->second->info->name); } data_.dynamicImplementations.erase(i); if (removeListener) { @@ -1659,9 +1812,9 @@ void ServiceManager::removeImplementation(rtl::OUString name) { assert(i->second.get() != 0); //TODO: The below leaves data_ in an inconsistent state upon exceptions: removeFromImplementationMap( - &data_.services, i->second->services, i->second); + &data_.services, i->second->info->services, i->second); removeFromImplementationMap( - &data_.singletons, i->second->singletons, i->second); + &data_.singletons, i->second->info->singletons, i->second); for (DynamicImplementations::iterator j( data_.dynamicImplementations.begin()); j != data_.dynamicImplementations.end(); ++j) @@ -1694,7 +1847,7 @@ boost::shared_ptr< Implementation > ServiceManager::findServiceImplementation( assert(!i->second.empty()); SAL_INFO_IF( i->second.size() > 1, "cppuhelper", - "Arbitrarily chosing " << i->second[0]->name + "Arbitrarily chosing " << i->second[0]->info->name << " among multiple implementations for " << i->first); impl = i->second[0]; } @@ -1705,82 +1858,124 @@ boost::shared_ptr< Implementation > ServiceManager::findServiceImplementation( // while the mutex is unlocked and loading can thus fail, as the entity from // which to load can disappear once the service factory is removed. if (!loaded) { - loadImplementation(impl.get()); + css::uno::Reference< css::lang::XSingleComponentFactory > f1; + css::uno::Reference< css::lang::XSingleServiceFactory > f2; + loadImplementation(impl->info, &f1, &f2); + osl::MutexGuard g(rBHelper.rMutex); + if (!(isDisposed() || impl->loaded)) { + impl->loaded = true; + impl->factory1 = f1; + impl->factory2 = f2; + } } return impl; } -void ServiceManager::loadImplementation(Implementation * implementation) { - assert(implementation != 0); - rtl::OUString uri; - try { - uri = cppu::bootstrap_expandUri(implementation->uri); - } catch (css::lang::IllegalArgumentException & e) { - throw css::uno::DeploymentException( - "Cannot expand URI" + implementation->uri + ": " + e.Message, +css::uno::Reference< css::uno::XInterface > +FactoryWrapper::createInstanceWithContext( + css::uno::Reference< css::uno::XComponentContext > const & Context) + throw (css::uno::Exception, css::uno::RuntimeException) +{ + loadImplementation(); + return factory1_.is() + ? factory1_->createInstanceWithContext(Context) + : factory2_->createInstance(); +} + +css::uno::Reference< css::uno::XInterface > +FactoryWrapper::createInstanceWithArgumentsAndContext( + css::uno::Sequence< css::uno::Any > const & Arguments, + css::uno::Reference< css::uno::XComponentContext > const & Context) + throw (css::uno::Exception, css::uno::RuntimeException) +{ + loadImplementation(); + return factory1_.is() + ? factory1_->createInstanceWithArgumentsAndContext(Arguments, Context) + : factory2_->createInstanceWithArguments(Arguments); +} + +css::uno::Reference< css::uno::XInterface > FactoryWrapper::createInstance() + throw (css::uno::Exception, css::uno::RuntimeException) +{ + loadImplementation(); + return factory1_.is() + ? factory1_->createInstanceWithContext(manager_->getContext()) + : factory2_->createInstance(); +} + +css::uno::Reference< css::uno::XInterface > +FactoryWrapper::createInstanceWithArguments( + css::uno::Sequence< css::uno::Any > const & Arguments) + throw (css::uno::Exception, css::uno::RuntimeException) +{ + loadImplementation(); + return factory1_.is() + ? factory1_->createInstanceWithArgumentsAndContext( + Arguments, manager_->getContext()) + : factory2_->createInstanceWithArguments(Arguments); +} + +rtl::OUString FactoryWrapper::getImplementationName() + throw (css::uno::RuntimeException) +{ + return info_->name; +} + +sal_Bool FactoryWrapper::supportsService(rtl::OUString const & ServiceName) + throw (css::uno::RuntimeException) +{ + css::uno::Sequence< rtl::OUString > names(getSupportedServiceNames()); + for (sal_Int32 i = 0; i != names.getLength(); ++i) { + if (ServiceName == names[i]) { + return true; + } + } + return false; +} + +css::uno::Sequence< rtl::OUString > FactoryWrapper::getSupportedServiceNames() + throw (css::uno::RuntimeException) +{ + if (info_->services.size() > static_cast< sal_uInt32 >(SAL_MAX_INT32)) { + throw css::uno::RuntimeException( + "Implementation " + info_->name + " supports too many services", static_cast< cppu::OWeakObject * >(this)); } - css::uno::Reference< css::uno::XInterface > f0; - // Shortcut loading via SharedLibrary loader, to pass in prefix argument - // (which the loader's activate implementation would normally obtain through - // the legacy xKey argument): - if (!implementation->alienContext.is() - && implementation->loader == "com.sun.star.loader.SharedLibrary") + css::uno::Sequence< rtl::OUString > names( + static_cast< sal_Int32 >(info_->services.size())); + sal_Int32 i = 0; + for (std::vector< rtl::OUString >::const_iterator j( + info_->services.begin()); + j != info_->services.end(); ++j) { - rtl::OUString prefix(implementation->prefix); - if (!prefix.isEmpty()) { - prefix += "_"; - } - try { - f0 = cppu::loadSharedLibComponentFactory( - uri, rtl::OUString(), implementation->name, this, - css::uno::Reference< css::registry::XRegistryKey >(), prefix); - } catch (css::loader::CannotActivateFactoryException & e) { - throw css::uno::DeploymentException( - "Cannot activate implementation " + uri + ": " + e.Message, - static_cast< cppu::OWeakObject * >(this)); - } - } else { - SAL_INFO_IF( - !implementation->prefix.isEmpty(), "cppuhelper", - "Loader " << implementation->loader << " and non-empty prefix " - << implementation->prefix); - css::uno::Reference< css::uno::XComponentContext > ctxt; - css::uno::Reference< css::lang::XMultiComponentFactory > smgr; - if (implementation->alienContext.is()) { - ctxt = implementation->alienContext; - smgr = css::uno::Reference< css::lang::XMultiComponentFactory >( - ctxt->getServiceManager(), css::uno::UNO_SET_THROW); - } else { - assert(context_.is()); - ctxt = context_; - smgr = this; - } - css::uno::Reference< css::loader::XImplementationLoader > loader( - smgr->createInstanceWithContext(implementation->loader, ctxt), - css::uno::UNO_QUERY_THROW); - try { - f0 = loader->activate( - implementation->name, rtl::OUString(), uri, - css::uno::Reference< css::registry::XRegistryKey >()); - } catch (css::loader::CannotActivateFactoryException & e) { - throw css::uno::DeploymentException( - "Cannot activate implementation " + uri + ": " + e.Message, - static_cast< cppu::OWeakObject * >(this)); + names[i++] = *j; + } + return names; +} + +void FactoryWrapper::loadImplementation() { + { + osl::MutexGuard g(mutex_); + if (loaded_) { + return; } } - css::uno::Reference< css::lang::XSingleComponentFactory > f1( - f0, css::uno::UNO_QUERY); + css::uno::Reference< css::lang::XSingleComponentFactory > f1; css::uno::Reference< css::lang::XSingleServiceFactory > f2; - if (!f1.is()) { - f2 = css::uno::Reference< css::lang::XSingleServiceFactory >( - f0, css::uno::UNO_QUERY); + //TODO: There is a race here, as the relevant service factory can already + // have been removed and loading can thus fail, as the entity from which to + // load can disappear once the service factory is removed: + manager_->loadImplementation(info_, &f1, &f2); + if (!(f1.is() || f2.is())) { + throw css::uno::DeploymentException( + "Implementation " + info_->name + " does not provide a factory", + static_cast< cppu::OWeakObject * >(this)); } - osl::MutexGuard g(rBHelper.rMutex); - if (!(isDisposed() || implementation->loaded)) { - implementation->loaded = true; - implementation->factory1 = f1; - implementation->factory2 = f2; + osl::MutexGuard g(mutex_); + if (!loaded_) { + loaded_ = true; + factory1_ = f1; + factory2_ = f2; } } @@ -1818,12 +2013,12 @@ css::uno::Reference< css::uno::XComponentContext > bootstrapComponentContext( assert(i->second[0].get() != 0); SAL_INFO_IF( i->second.size() > 1, "cppuhelper", - "Arbitrarily chosing " << i->second[0]->name + "Arbitrarily chosing " << i->second[0]->info->name << " among multiple implementations for " << i->first); context_values.push_back( cppu::ContextEntry_Init( "/singletons/" + i->first, - css::uno::makeAny(i->second[0]->name), true)); + css::uno::makeAny(i->second[0]->info->name), true)); } cppu::add_access_control_entries(&context_values, bootstrap); assert(!context_values.empty()); |