| /************************************************************** |
| * |
| * 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 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| #include "sal/config.h" |
| |
| #include <algorithm> |
| #include <cstddef> |
| #include <limits> |
| #include <memory> |
| #include <vector> |
| |
| #include "boost/noncopyable.hpp" |
| #include "com/sun/star/bridge/InvalidProtocolChangeException.hpp" |
| #include "com/sun/star/bridge/XBridge.hpp" |
| #include "com/sun/star/bridge/XInstanceProvider.hpp" |
| #include "com/sun/star/bridge/XProtocolProperties.hpp" |
| #include "com/sun/star/connection/XConnection.hpp" |
| #include "com/sun/star/io/IOException.hpp" |
| #include "com/sun/star/lang/DisposedException.hpp" |
| #include "com/sun/star/lang/EventObject.hpp" |
| #include "com/sun/star/lang/XEventListener.hpp" |
| #include "com/sun/star/uno/Reference.hxx" |
| #include "com/sun/star/uno/RuntimeException.hpp" |
| #include "com/sun/star/uno/Sequence.hxx" |
| #include "com/sun/star/uno/XInterface.hpp" |
| #include "cppuhelper/exc_hlp.hxx" |
| #include "cppuhelper/weak.hxx" |
| #include "osl/diagnose.h" |
| #include "osl/mutex.hxx" |
| #include "osl/thread.hxx" |
| #include "rtl/byteseq.hxx" |
| #include "rtl/random.h" |
| #include "rtl/ref.hxx" |
| #include "rtl/textenc.h" |
| #include "rtl/ustrbuf.hxx" |
| #include "rtl/ustring.h" |
| #include "rtl/ustring.hxx" |
| #include "sal/types.h" |
| #include "typelib/typeclass.h" |
| #include "typelib/typedescription.h" |
| #include "typelib/typedescription.hxx" |
| #include "uno/dispatcher.hxx" |
| #include "uno/environment.hxx" |
| #include "uno/lbnames.h" |
| |
| #include "binaryany.hxx" |
| #include "bridge.hxx" |
| #include "bridgefactory.hxx" |
| #include "incomingreply.hxx" |
| #include "lessoperators.hxx" |
| #include "outgoingrequest.hxx" |
| #include "outgoingrequests.hxx" |
| #include "proxy.hxx" |
| #include "reader.hxx" |
| |
| namespace binaryurp { |
| |
| namespace { |
| |
| namespace css = com::sun::star; |
| |
| sal_Int32 random() { |
| sal_Int32 n; |
| rtlRandomPool pool = rtl_random_createPool(); |
| rtl_random_getBytes(pool, &n, sizeof n); |
| rtl_random_destroyPool(pool); |
| return n; |
| } |
| |
| extern "C" void SAL_CALL freeProxyCallback(uno_ExtEnvironment *, void * pProxy) |
| { |
| OSL_ASSERT(pProxy != 0); |
| static_cast< Proxy * >(pProxy)->do_free(); |
| } |
| |
| void joinThread(osl::Thread * thread) { |
| OSL_ASSERT(thread != 0); |
| if (thread->getIdentifier() != osl::Thread::getCurrentIdentifier()) { |
| thread->join(); |
| } |
| } |
| |
| class AttachThread: private boost::noncopyable { |
| public: |
| explicit AttachThread(uno_ThreadPool threadPool); |
| |
| ~AttachThread(); |
| |
| rtl::ByteSequence getTid() throw (); |
| |
| private: |
| uno_ThreadPool threadPool_; |
| rtl::ByteSequence tid_; |
| }; |
| |
| AttachThread::AttachThread(uno_ThreadPool threadPool): threadPool_(threadPool) { |
| sal_Sequence * s = 0; |
| uno_getIdOfCurrentThread(&s); |
| tid_ = rtl::ByteSequence(s, rtl::BYTESEQ_NOACQUIRE); |
| uno_threadpool_attach(threadPool_); |
| } |
| |
| AttachThread::~AttachThread() { |
| uno_threadpool_detach(threadPool_); |
| uno_releaseIdFromCurrentThread(); |
| } |
| |
| rtl::ByteSequence AttachThread::getTid() throw () { |
| return tid_; |
| } |
| |
| class PopOutgoingRequest: private boost::noncopyable { |
| public: |
| PopOutgoingRequest( |
| OutgoingRequests & requests, rtl::ByteSequence const & tid, |
| OutgoingRequest const & request); |
| |
| ~PopOutgoingRequest(); |
| |
| void clear(); |
| |
| private: |
| OutgoingRequests & requests_; |
| rtl::ByteSequence tid_; |
| bool cleared_; |
| }; |
| |
| PopOutgoingRequest::PopOutgoingRequest( |
| OutgoingRequests & requests, rtl::ByteSequence const & tid, |
| OutgoingRequest const & request): |
| requests_(requests), tid_(tid), cleared_(false) |
| { |
| requests_.push(tid_, request); |
| } |
| |
| PopOutgoingRequest::~PopOutgoingRequest() { |
| if (!cleared_) { |
| requests_.pop(tid_); |
| } |
| } |
| |
| void PopOutgoingRequest::clear() { |
| cleared_ = true; |
| } |
| |
| } |
| |
| Bridge::Bridge( |
| rtl::Reference< BridgeFactory > const & factory, rtl::OUString const & name, |
| css::uno::Reference< css::connection::XConnection > const & connection, |
| css::uno::Reference< css::bridge::XInstanceProvider > const & provider): |
| factory_(factory), name_(name), connection_(connection), |
| provider_(provider), |
| binaryUno_(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(UNO_LB_UNO))), |
| cppToBinaryMapping_( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM(CPPU_CURRENT_LANGUAGE_BINDING_NAME)), |
| rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(UNO_LB_UNO))), |
| binaryToCppMapping_( |
| rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(UNO_LB_UNO)), |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM(CPPU_CURRENT_LANGUAGE_BINDING_NAME))), |
| protPropTid_( |
| reinterpret_cast< sal_Int8 const * >(".UrpProtocolPropertiesTid"), |
| RTL_CONSTASCII_LENGTH(".UrpProtocolPropertiesTid")), |
| protPropOid_(RTL_CONSTASCII_USTRINGPARAM("UrpProtocolProperties")), |
| protPropType_( |
| cppu::UnoType< |
| css::uno::Reference< css::bridge::XProtocolProperties > >::get()), |
| protPropRequest_( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "com.sun.star.bridge.XProtocolProperties::requestChange"))), |
| protPropCommit_( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "com.sun.star.bridge.XProtocolProperties::commitChange"))), |
| threadPool_(0), currentContextMode_(false), proxies_(0), calls_(0), |
| normalCall_(false), activeCalls_(0), terminated_(false), |
| mode_(MODE_REQUESTED) |
| { |
| OSL_ASSERT(factory.is() && connection.is()); |
| if (!binaryUno_.is()) { |
| throw css::uno::RuntimeException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM("URP: no binary UNO environment")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| if (!(cppToBinaryMapping_.is() && binaryToCppMapping_.is())) { |
| throw css::uno::RuntimeException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM("URP: no C++ UNO mapping")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| passive_.set(); |
| } |
| |
| void Bridge::start() { |
| OSL_ASSERT(threadPool_ == 0 && !writer_.is() && !reader_.is()); |
| threadPool_ = uno_threadpool_create(); |
| OSL_ASSERT(threadPool_ != 0); |
| writer_.set(new Writer(this)); |
| writer_->create(); |
| reader_.set(new Reader(this)); |
| reader_->create(); |
| } |
| |
| void Bridge::terminate() { |
| rtl::Reference< Reader > r; |
| rtl::Reference< Writer > w; |
| Listeners ls; |
| { |
| osl::MutexGuard g(mutex_); |
| if (terminated_) { |
| return; |
| } |
| std::swap(reader_, r); |
| std::swap(writer_, w); |
| ls.swap(listeners_); |
| terminated_ = true; |
| } |
| try { |
| connection_->close(); |
| } catch (css::io::IOException & e) { |
| OSL_TRACE( |
| OSL_LOG_PREFIX "caught IO exception '%s'", |
| rtl::OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8).getStr()); |
| } |
| OSL_ASSERT(w.is()); |
| w->stop(); |
| joinThread(r.get()); |
| joinThread(w.get()); |
| OSL_ASSERT(threadPool_ != 0); |
| uno_threadpool_dispose(threadPool_); |
| Stubs s; |
| { |
| osl::MutexGuard g(mutex_); |
| s.swap(stubs_); |
| } |
| for (Stubs::iterator i(s.begin()); i != s.end(); ++i) { |
| for (Stub::iterator j(i->second.begin()); j != i->second.end(); ++j) { |
| binaryUno_.get()->pExtEnv->revokeInterface( |
| binaryUno_.get()->pExtEnv, j->second.object.get()); |
| } |
| } |
| factory_->removeBridge(this); |
| for (Listeners::iterator i(ls.begin()); i != ls.end(); ++i) { |
| try { |
| (*i)->disposing( |
| css::lang::EventObject( |
| static_cast< cppu::OWeakObject * >(this))); |
| } catch (css::uno::RuntimeException & e) { |
| OSL_TRACE( |
| OSL_LOG_PREFIX "caught runtime exception '%s'", |
| rtl::OUStringToOString( |
| e.Message, RTL_TEXTENCODING_UTF8).getStr()); |
| } |
| } |
| } |
| |
| css::uno::Reference< css::connection::XConnection > Bridge::getConnection() |
| const |
| { |
| return connection_; |
| } |
| |
| css::uno::Reference< css::bridge::XInstanceProvider > Bridge::getProvider() |
| const |
| { |
| return provider_; |
| } |
| |
| css::uno::Mapping & Bridge::getCppToBinaryMapping() { |
| return cppToBinaryMapping_; |
| } |
| |
| BinaryAny Bridge::mapCppToBinaryAny(css::uno::Any const & cppAny) { |
| css::uno::Any in(cppAny); |
| BinaryAny out; |
| out.~BinaryAny(); |
| uno_copyAndConvertData( |
| out.get(), &in, |
| css::uno::TypeDescription(cppu::UnoType< css::uno::Any >::get()).get(), |
| cppToBinaryMapping_.get()); |
| return out; |
| } |
| |
| uno_ThreadPool Bridge::getThreadPool() const { |
| OSL_ASSERT(threadPool_ != 0); |
| return threadPool_; |
| } |
| |
| rtl::Reference< Writer > Bridge::getWriter() { |
| osl::MutexGuard g(mutex_); |
| if (terminated_) { |
| throw css::lang::DisposedException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "Binary URP bridge already disposed")), |
| static_cast< cppu::OWeakObject * >(this)); |
| } |
| OSL_ASSERT(writer_.is()); |
| return writer_; |
| } |
| |
| css::uno::UnoInterfaceReference Bridge::registerIncomingInterface( |
| rtl::OUString const & oid, css::uno::TypeDescription const & type) |
| { |
| OSL_ASSERT(type.is()); |
| if ( oid.isEmpty() ) { |
| return css::uno::UnoInterfaceReference(); |
| } |
| css::uno::UnoInterfaceReference obj(findStub(oid, type)); |
| if (!obj.is()) { |
| binaryUno_.get()->pExtEnv->getRegisteredInterface( |
| binaryUno_.get()->pExtEnv, |
| reinterpret_cast< void ** >(&obj.m_pUnoI), oid.pData, |
| reinterpret_cast< typelib_InterfaceTypeDescription * >(type.get())); |
| if (obj.is()) { |
| makeReleaseCall(oid, type); |
| } else { |
| obj.set(new Proxy(this, oid, type), SAL_NO_ACQUIRE); |
| { |
| osl::MutexGuard g(mutex_); |
| OSL_ASSERT( |
| proxies_ < std::numeric_limits< std::size_t >::max()); |
| ++proxies_; |
| } |
| binaryUno_.get()->pExtEnv->registerProxyInterface( |
| binaryUno_.get()->pExtEnv, |
| reinterpret_cast< void ** >(&obj.m_pUnoI), &freeProxyCallback, |
| oid.pData, |
| reinterpret_cast< typelib_InterfaceTypeDescription * >( |
| type.get())); |
| } |
| } |
| return obj; |
| } |
| |
| rtl::OUString Bridge::registerOutgoingInterface( |
| css::uno::UnoInterfaceReference const & object, |
| css::uno::TypeDescription const & type) |
| { |
| OSL_ASSERT(type.is()); |
| if (!object.is()) { |
| return rtl::OUString(); |
| } |
| rtl::OUString oid; |
| if (!Proxy::isProxy(this, object, &oid)) { |
| binaryUno_.get()->pExtEnv->getObjectIdentifier( |
| binaryUno_.get()->pExtEnv, &oid.pData, object.get()); |
| osl::MutexGuard g(mutex_); |
| Stubs::iterator i(stubs_.find(oid)); |
| Stub newStub; |
| Stub * stub = i == stubs_.end() ? &newStub : &i->second; |
| Stub::iterator j(stub->find(type)); |
| //TODO: Release sub-stub if it is not successfully sent to remote side |
| // (otherwise, stub will leak until terminate()): |
| if (j == stub->end()) { |
| j = stub->insert(Stub::value_type(type, SubStub())).first; |
| if (stub == &newStub) { |
| i = stubs_.insert(Stubs::value_type(oid, Stub())).first; |
| std::swap(i->second, newStub); |
| j = i->second.find(type); |
| OSL_ASSERT(j != i->second.end()); |
| } |
| j->second.object = object; |
| j->second.references = 1; |
| binaryUno_.get()->pExtEnv->registerInterface( |
| binaryUno_.get()->pExtEnv, |
| reinterpret_cast< void ** >(&j->second.object.m_pUnoI), |
| oid.pData, |
| reinterpret_cast< typelib_InterfaceTypeDescription * >( |
| type.get())); |
| } else { |
| OSL_ASSERT(stub != &newStub); |
| if (j->second.references == SAL_MAX_UINT32) { |
| throw css::uno::RuntimeException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "URP: stub reference count overflow")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| ++j->second.references; |
| } |
| } |
| return oid; |
| } |
| |
| css::uno::UnoInterfaceReference Bridge::findStub( |
| rtl::OUString const & oid, css::uno::TypeDescription const & type) |
| { |
| OSL_ASSERT(!oid.isEmpty() && type.is()); |
| osl::MutexGuard g(mutex_); |
| Stubs::iterator i(stubs_.find(oid)); |
| if (i != stubs_.end()) { |
| Stub::iterator j(i->second.find(type)); |
| if (j != i->second.end()) { |
| return j->second.object; |
| } |
| for (j = i->second.begin(); j != i->second.end(); ++j) { |
| if (typelib_typedescription_isAssignableFrom( |
| type.get(), j->first.get())) |
| { |
| return j->second.object; |
| } |
| } |
| } |
| return css::uno::UnoInterfaceReference(); |
| } |
| |
| void Bridge::releaseStub( |
| rtl::OUString const & oid, css::uno::TypeDescription const & type) |
| { |
| OSL_ASSERT(!oid.isEmpty() && type.is()); |
| css::uno::UnoInterfaceReference obj; |
| bool unused; |
| { |
| osl::MutexGuard g(mutex_); |
| Stubs::iterator i(stubs_.find(oid)); |
| if (i == stubs_.end()) { |
| throw css::uno::RuntimeException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM("URP: release unknown stub")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| Stub::iterator j(i->second.find(type)); |
| if (j == i->second.end()) { |
| throw css::uno::RuntimeException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM("URP: release unknown stub")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| OSL_ASSERT(j->second.references > 0); |
| --j->second.references; |
| if (j->second.references == 0) { |
| obj = j->second.object; |
| i->second.erase(j); |
| if (i->second.empty()) { |
| stubs_.erase(i); |
| } |
| } |
| unused = becameUnused(); |
| } |
| if (obj.is()) { |
| binaryUno_.get()->pExtEnv->revokeInterface( |
| binaryUno_.get()->pExtEnv, obj.get()); |
| } |
| terminateWhenUnused(unused); |
| } |
| |
| void Bridge::resurrectProxy(Proxy & proxy) { |
| uno_Interface * p = &proxy; |
| binaryUno_.get()->pExtEnv->registerProxyInterface( |
| binaryUno_.get()->pExtEnv, |
| reinterpret_cast< void ** >(&p), &freeProxyCallback, |
| proxy.getOid().pData, |
| reinterpret_cast< typelib_InterfaceTypeDescription * >( |
| proxy.getType().get())); |
| OSL_ASSERT(p == &proxy); |
| } |
| |
| void Bridge::revokeProxy(Proxy & proxy) { |
| binaryUno_.get()->pExtEnv->revokeInterface( |
| binaryUno_.get()->pExtEnv, &proxy); |
| } |
| |
| void Bridge::freeProxy(Proxy & proxy) { |
| try { |
| makeReleaseCall(proxy.getOid(), proxy.getType()); |
| } catch (css::uno::RuntimeException & e) { |
| OSL_TRACE( |
| OSL_LOG_PREFIX "caught runtime exception '%s'", |
| rtl::OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8).getStr()); |
| } catch (std::exception & e) { |
| OSL_TRACE(OSL_LOG_PREFIX "caught C++ exception '%s'", e.what()); |
| } |
| bool unused; |
| { |
| osl::MutexGuard g(mutex_); |
| OSL_ASSERT(proxies_ > 0); |
| --proxies_; |
| unused = becameUnused(); |
| } |
| terminateWhenUnused(unused); |
| } |
| |
| void Bridge::incrementCalls(bool normalCall) throw () { |
| osl::MutexGuard g(mutex_); |
| OSL_ASSERT(calls_ < std::numeric_limits< std::size_t >::max()); |
| ++calls_; |
| normalCall_ |= normalCall; |
| } |
| |
| void Bridge::decrementCalls() { |
| bool unused; |
| { |
| osl::MutexGuard g(mutex_); |
| OSL_ASSERT(calls_ > 0); |
| --calls_; |
| unused = becameUnused(); |
| } |
| terminateWhenUnused(unused); |
| } |
| |
| void Bridge::incrementActiveCalls() throw () { |
| osl::MutexGuard g(mutex_); |
| OSL_ASSERT( |
| activeCalls_ <= calls_ && |
| activeCalls_ < std::numeric_limits< std::size_t >::max()); |
| ++activeCalls_; |
| passive_.reset(); |
| } |
| |
| void Bridge::decrementActiveCalls() throw () { |
| osl::MutexGuard g(mutex_); |
| OSL_ASSERT(activeCalls_ <= calls_ && activeCalls_ > 0); |
| --activeCalls_; |
| if (activeCalls_ == 0) { |
| passive_.set(); |
| } |
| } |
| |
| bool Bridge::makeCall( |
| rtl::OUString const & oid, css::uno::TypeDescription const & member, |
| bool setter, std::vector< BinaryAny > const & inArguments, |
| BinaryAny * returnValue, std::vector< BinaryAny > * outArguments) |
| { |
| std::auto_ptr< IncomingReply > resp; |
| { |
| AttachThread att(threadPool_); |
| PopOutgoingRequest pop( |
| outgoingRequests_, att.getTid(), |
| OutgoingRequest(OutgoingRequest::KIND_NORMAL, member, setter)); |
| sendRequest( |
| att.getTid(), oid, css::uno::TypeDescription(), member, |
| inArguments); |
| pop.clear(); |
| incrementCalls(true); |
| incrementActiveCalls(); |
| void * job; |
| uno_threadpool_enter(threadPool_, &job); |
| resp.reset(static_cast< IncomingReply * >(job)); |
| decrementActiveCalls(); |
| decrementCalls(); |
| } |
| if (resp.get() == 0) { |
| throw css::lang::DisposedException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "Binary URP bridge disposed during call")), |
| static_cast< cppu::OWeakObject * >(this)); |
| } |
| *returnValue = resp->returnValue; |
| if (!resp->exception) { |
| *outArguments = resp->outArguments; |
| } |
| return resp->exception; |
| } |
| |
| void Bridge::sendRequestChangeRequest() { |
| OSL_ASSERT(mode_ == MODE_REQUESTED); |
| random_ = random(); |
| std::vector< BinaryAny > a; |
| a.push_back( |
| BinaryAny( |
| css::uno::TypeDescription(cppu::UnoType< sal_Int32 >::get()), |
| &random_)); |
| sendProtPropRequest(OutgoingRequest::KIND_REQUEST_CHANGE, a); |
| } |
| |
| void Bridge::handleRequestChangeReply( |
| bool exception, BinaryAny const & returnValue) |
| { |
| throwException(exception, returnValue); |
| sal_Int32 n = *static_cast< sal_Int32 * >( |
| returnValue.getValue( |
| css::uno::TypeDescription(cppu::UnoType< sal_Int32 >::get()))); |
| sal_Int32 exp = 0; |
| switch (mode_) { |
| case MODE_REQUESTED: |
| case MODE_REPLY_1: |
| exp = 1; |
| break; |
| case MODE_REPLY_MINUS1: |
| exp = -1; |
| mode_ = MODE_REQUESTED; |
| break; |
| case MODE_REPLY_0: |
| exp = 0; |
| mode_ = MODE_WAIT; |
| break; |
| default: |
| OSL_ASSERT(false); // this cannot happen |
| break; |
| } |
| if (n != exp) { |
| throw css::uno::RuntimeException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "URP: requestChange reply with unexpected return value" |
| " received")), |
| static_cast< cppu::OWeakObject * >(this)); |
| } |
| decrementCalls(); |
| switch (exp) { |
| case -1: |
| sendRequestChangeRequest(); |
| break; |
| case 0: |
| break; |
| case 1: |
| sendCommitChangeRequest(); |
| break; |
| default: |
| OSL_ASSERT(false); // this cannot happen |
| break; |
| } |
| } |
| |
| void Bridge::handleCommitChangeReply( |
| bool exception, BinaryAny const & returnValue) |
| { |
| bool ccMode = true; |
| try { |
| throwException(exception, returnValue); |
| } catch (css::bridge::InvalidProtocolChangeException &) { |
| ccMode = false; |
| } |
| if (ccMode) { |
| setCurrentContextMode(); |
| } |
| OSL_ASSERT(mode_ == MODE_REQUESTED || mode_ == MODE_REPLY_1); |
| mode_ = MODE_NORMAL; |
| getWriter()->unblock(); |
| decrementCalls(); |
| } |
| |
| void Bridge::handleRequestChangeRequest( |
| rtl::ByteSequence const & tid, std::vector< BinaryAny > const & inArguments) |
| { |
| OSL_ASSERT(inArguments.size() == 1); |
| switch (mode_) { |
| case MODE_REQUESTED: |
| { |
| sal_Int32 n2 = *static_cast< sal_Int32 * >( |
| inArguments[0].getValue( |
| css::uno::TypeDescription( |
| cppu::UnoType< sal_Int32 >::get()))); |
| sal_Int32 ret; |
| if (n2 > random_) { |
| ret = 1; |
| mode_ = MODE_REPLY_0; |
| } else if (n2 == random_) { |
| ret = -1; |
| mode_ = MODE_REPLY_MINUS1; |
| } else { |
| ret = 0; |
| mode_ = MODE_REPLY_1; |
| } |
| getWriter()->sendDirectReply( |
| tid, protPropRequest_, false, |
| BinaryAny( |
| css::uno::TypeDescription( |
| cppu::UnoType< sal_Int32 >::get()), |
| &ret), |
| std::vector< BinaryAny >()); |
| break; |
| } |
| case MODE_NORMAL: |
| { |
| mode_ = MODE_NORMAL_WAIT; |
| sal_Int32 ret = 1; |
| getWriter()->queueReply( |
| tid, protPropRequest_, false, false, |
| BinaryAny( |
| css::uno::TypeDescription( |
| cppu::UnoType< sal_Int32 >::get()), |
| &ret), |
| std::vector< BinaryAny >(), false); |
| break; |
| } |
| default: |
| throw css::uno::RuntimeException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "URP: unexpected requestChange request received")), |
| static_cast< cppu::OWeakObject * >(this)); |
| } |
| } |
| |
| void Bridge::handleCommitChangeRequest( |
| rtl::ByteSequence const & tid, std::vector< BinaryAny > const & inArguments) |
| { |
| bool ccMode = false; |
| bool exc = false; |
| BinaryAny ret; |
| OSL_ASSERT(inArguments.size() == 1); |
| css::uno::Sequence< css::bridge::ProtocolProperty > s; |
| OSL_VERIFY(mapBinaryToCppAny(inArguments[0]) >>= s); |
| for (sal_Int32 i = 0; i != s.getLength(); ++i) { |
| if (s[i].Name.equalsAsciiL( |
| RTL_CONSTASCII_STRINGPARAM("CurrentContext"))) |
| { |
| ccMode = true; |
| } else { |
| ccMode = false; |
| exc = true; |
| ret = mapCppToBinaryAny( |
| css::uno::makeAny( |
| css::bridge::InvalidProtocolChangeException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "InvalidProtocolChangeException")), |
| css::uno::Reference< css::uno::XInterface >(), s[i], |
| 1))); |
| break; |
| } |
| } |
| switch (mode_) { |
| case MODE_WAIT: |
| getWriter()->sendDirectReply( |
| tid, protPropCommit_, exc, ret, std::vector< BinaryAny >()); |
| if (ccMode) { |
| setCurrentContextMode(); |
| mode_ = MODE_NORMAL; |
| getWriter()->unblock(); |
| } else { |
| mode_ = MODE_REQUESTED; |
| sendRequestChangeRequest(); |
| } |
| break; |
| case MODE_NORMAL_WAIT: |
| getWriter()->queueReply( |
| tid, protPropCommit_, false, false, ret, std::vector< BinaryAny >(), |
| ccMode); |
| mode_ = MODE_NORMAL; |
| break; |
| default: |
| throw css::uno::RuntimeException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "URP: unexpected commitChange request received")), |
| static_cast< cppu::OWeakObject * >(this)); |
| } |
| } |
| |
| OutgoingRequest Bridge::lastOutgoingRequest(rtl::ByteSequence const & tid) { |
| OutgoingRequest req(outgoingRequests_.top(tid)); |
| outgoingRequests_.pop(tid); |
| return req; |
| } |
| |
| bool Bridge::isProtocolPropertiesRequest( |
| rtl::OUString const & oid, css::uno::TypeDescription const & type) const |
| { |
| return oid == protPropOid_ && type.equals(protPropType_); |
| } |
| |
| void Bridge::setCurrentContextMode() { |
| osl::MutexGuard g(mutex_); |
| currentContextMode_ = true; |
| } |
| |
| bool Bridge::isCurrentContextMode() { |
| osl::MutexGuard g(mutex_); |
| return currentContextMode_; |
| } |
| |
| Bridge::~Bridge() { |
| if (threadPool_ != 0) { |
| uno_threadpool_destroy(threadPool_); |
| } |
| } |
| |
| css::uno::Reference< css::uno::XInterface > Bridge::getInstance( |
| rtl::OUString const & sInstanceName) throw (css::uno::RuntimeException) |
| { |
| if ( sInstanceName.isEmpty() ) { |
| throw css::uno::RuntimeException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "XBridge::getInstance sInstanceName must be non-empty")), |
| static_cast< cppu::OWeakObject * >(this)); |
| } |
| for (sal_Int32 i = 0; i != sInstanceName.getLength(); ++i) { |
| if (sInstanceName[i] > 0x7F) { |
| throw css::io::IOException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "XBridge::getInstance sInstanceName contains non-ASCII" |
| " character")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| } |
| css::uno::TypeDescription ifc( |
| cppu::UnoType< css::uno::Reference< css::uno::XInterface > >::get()); |
| typelib_TypeDescription * p = ifc.get(); |
| std::vector< BinaryAny > inArgs; |
| inArgs.push_back( |
| BinaryAny( |
| css::uno::TypeDescription(cppu::UnoType< css::uno::Type >::get()), |
| &p)); |
| BinaryAny ret; |
| std::vector< BinaryAny> outArgs; |
| bool exc = makeCall( |
| sInstanceName, |
| css::uno::TypeDescription( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "com.sun.star.uno.XInterface::queryInterface"))), |
| false, inArgs, &ret, &outArgs); |
| throwException(exc, ret); |
| return css::uno::Reference< css::uno::XInterface >( |
| static_cast< css::uno::XInterface * >( |
| binaryToCppMapping_.mapInterface( |
| *static_cast< uno_Interface ** >(ret.getValue(ifc)), |
| ifc.get())), |
| css::uno::UNO_REF_NO_ACQUIRE); |
| } |
| |
| rtl::OUString Bridge::getName() throw (css::uno::RuntimeException) { |
| return name_; |
| } |
| |
| rtl::OUString Bridge::getDescription() throw (css::uno::RuntimeException) { |
| rtl::OUStringBuffer b(name_); |
| b.append(sal_Unicode(':')); |
| b.append(connection_->getDescription()); |
| return b.makeStringAndClear(); |
| } |
| |
| void Bridge::dispose() throw (css::uno::RuntimeException) { |
| terminate(); |
| // OOo expects dispose to not return while there are still remote calls in |
| // progress; an external protocol must ensure that dispose is not called |
| // from within an incoming or outgoing remote call, as passive_.wait() would |
| // otherwise deadlock: |
| passive_.wait(); |
| } |
| |
| void Bridge::addEventListener( |
| css::uno::Reference< css::lang::XEventListener > const & xListener) |
| throw (css::uno::RuntimeException) |
| { |
| OSL_ASSERT(xListener.is()); |
| { |
| osl::MutexGuard g(mutex_); |
| if (!terminated_) { |
| listeners_.push_back(xListener); |
| return; |
| } |
| } |
| xListener->disposing( |
| css::lang::EventObject(static_cast< cppu::OWeakObject * >(this))); |
| } |
| |
| void Bridge::removeEventListener( |
| css::uno::Reference< css::lang::XEventListener > const & aListener) |
| throw (css::uno::RuntimeException) |
| { |
| osl::MutexGuard g(mutex_); |
| Listeners::iterator i( |
| std::find(listeners_.begin(), listeners_.end(), aListener)); |
| if (i != listeners_.end()) { |
| listeners_.erase(i); |
| } |
| } |
| |
| void Bridge::sendCommitChangeRequest() { |
| OSL_ASSERT(mode_ == MODE_REQUESTED || mode_ == MODE_REPLY_1); |
| css::uno::Sequence< css::bridge::ProtocolProperty > s(1); |
| s[0].Name = rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("CurrentContext")); |
| std::vector< BinaryAny > a; |
| a.push_back(mapCppToBinaryAny(css::uno::makeAny(s))); |
| sendProtPropRequest(OutgoingRequest::KIND_COMMIT_CHANGE, a); |
| } |
| |
| void Bridge::sendProtPropRequest( |
| OutgoingRequest::Kind kind, std::vector< BinaryAny > const & inArguments) |
| { |
| OSL_ASSERT( |
| kind == OutgoingRequest::KIND_REQUEST_CHANGE || |
| kind == OutgoingRequest::KIND_COMMIT_CHANGE); |
| incrementCalls(false); |
| css::uno::TypeDescription member( |
| kind == OutgoingRequest::KIND_REQUEST_CHANGE |
| ? protPropRequest_ : protPropCommit_); |
| PopOutgoingRequest pop( |
| outgoingRequests_, protPropTid_, OutgoingRequest(kind, member, false)); |
| getWriter()->sendDirectRequest( |
| protPropTid_, protPropOid_, protPropType_, member, inArguments); |
| pop.clear(); |
| } |
| |
| void Bridge::makeReleaseCall( |
| rtl::OUString const & oid, css::uno::TypeDescription const & type) |
| { |
| AttachThread att(threadPool_); |
| sendRequest( |
| att.getTid(), oid, type, |
| css::uno::TypeDescription( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "com.sun.star.uno.XInterface::release"))), |
| std::vector< BinaryAny >()); |
| } |
| |
| void Bridge::sendRequest( |
| rtl::ByteSequence const & tid, rtl::OUString const & oid, |
| css::uno::TypeDescription const & type, |
| css::uno::TypeDescription const & member, |
| std::vector< BinaryAny > const & inArguments) |
| { |
| getWriter()->queueRequest(tid, oid, type, member, inArguments); |
| } |
| |
| void Bridge::throwException(bool exception, BinaryAny const & value) { |
| if (exception) { |
| cppu::throwException(mapBinaryToCppAny(value)); |
| } |
| } |
| |
| css::uno::Any Bridge::mapBinaryToCppAny(BinaryAny const & binaryAny) { |
| BinaryAny in(binaryAny); |
| css::uno::Any out; |
| out.~Any(); |
| uno_copyAndConvertData( |
| &out, in.get(), |
| css::uno::TypeDescription(cppu::UnoType< css::uno::Any >::get()).get(), |
| binaryToCppMapping_.get()); |
| return out; |
| } |
| |
| bool Bridge::becameUnused() const { |
| return stubs_.empty() && proxies_ == 0 && calls_ == 0 && normalCall_; |
| } |
| |
| void Bridge::terminateWhenUnused(bool unused) { |
| if (unused) { |
| // That the current thread considers the bridge unused implies that it |
| // is not within an incoming or outgoing remote call (so calling |
| // terminate cannot lead to deadlock): |
| terminate(); |
| } |
| } |
| |
| } |