| /************************************************************** |
| * |
| * 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 <exception> |
| #include <vector> |
| |
| #include "com/sun/star/connection/XConnection.hpp" |
| #include "com/sun/star/lang/WrappedTargetRuntimeException.hpp" |
| #include "com/sun/star/uno/XCurrentContext.hpp" |
| #include "cppuhelper/exc_hlp.hxx" |
| #include "osl/mutex.hxx" |
| #include "rtl/memory.h" |
| #include "uno/dispatcher.hxx" |
| |
| #include "binaryany.hxx" |
| #include "bridge.hxx" |
| #include "currentcontext.hxx" |
| #include "specialfunctionids.hxx" |
| #include "writer.hxx" |
| |
| namespace binaryurp { |
| |
| namespace { |
| |
| namespace css = com::sun::star; |
| |
| bool isProtocolPropertyMessage(rtl::OUString const & oid) { |
| return oid.equalsAsciiL( |
| RTL_CONSTASCII_STRINGPARAM("UrpProtocolProperties")); |
| } |
| |
| } |
| |
| Writer::Item::Item() {} |
| |
| Writer::Item::Item( |
| rtl::ByteSequence const & theTid, rtl::OUString const & theOid, |
| css::uno::TypeDescription const & theType, |
| css::uno::TypeDescription const & theMember, |
| std::vector< BinaryAny > const & inArguments, |
| css::uno::UnoInterfaceReference const & theCurrentContext): |
| request(true), tid(theTid), oid(theOid), type(theType), member(theMember), |
| arguments(inArguments), currentContext(theCurrentContext) |
| {} |
| |
| Writer::Item::Item( |
| rtl::ByteSequence const & theTid, |
| css::uno::TypeDescription const & theMember, bool theSetter, |
| bool theException, BinaryAny const & theReturnValue, |
| std::vector< BinaryAny > const & outArguments, |
| bool theSetCurrentContextMode): |
| request(false), tid(theTid), member(theMember), setter(theSetter), |
| arguments(outArguments), exception(theException), |
| returnValue(theReturnValue), setCurrentContextMode(theSetCurrentContextMode) |
| {} |
| |
| Writer::Writer(rtl::Reference< Bridge > const & bridge): |
| bridge_(bridge), marshal_(bridge, state_), stop_(false) |
| { |
| OSL_ASSERT(bridge.is()); |
| acquire(); |
| } |
| |
| void Writer::sendDirectRequest( |
| rtl::ByteSequence const & tid, rtl::OUString const & oid, |
| css::uno::TypeDescription const & type, |
| css::uno::TypeDescription const & member, |
| std::vector< BinaryAny > const & inArguments) |
| { |
| OSL_ASSERT(!unblocked_.check()); |
| sendRequest( |
| tid, oid, type, member, inArguments, false, |
| css::uno::UnoInterfaceReference()); |
| } |
| |
| void Writer::sendDirectReply( |
| rtl::ByteSequence const & tid, css::uno::TypeDescription const & member, |
| bool exception, BinaryAny const & returnValue, |
| std::vector< BinaryAny > const & outArguments) |
| { |
| OSL_ASSERT(!unblocked_.check()); |
| sendReply(tid, member, false, exception, returnValue,outArguments); |
| } |
| |
| void Writer::queueRequest( |
| rtl::ByteSequence const & tid, rtl::OUString const & oid, |
| css::uno::TypeDescription const & type, |
| css::uno::TypeDescription const & member, |
| std::vector< BinaryAny > const & inArguments) |
| { |
| css::uno::UnoInterfaceReference cc(current_context::get()); |
| osl::MutexGuard g(mutex_); |
| queue_.push_back(Item(tid, oid, type, member, inArguments, cc)); |
| items_.set(); |
| } |
| |
| void Writer::queueReply( |
| rtl::ByteSequence const & tid, |
| com::sun::star::uno::TypeDescription const & member, bool setter, |
| bool exception, BinaryAny const & returnValue, |
| std::vector< BinaryAny > const & outArguments, bool setCurrentContextMode) |
| { |
| osl::MutexGuard g(mutex_); |
| queue_.push_back( |
| Item( |
| tid, member, setter, exception, returnValue, outArguments, |
| setCurrentContextMode)); |
| items_.set(); |
| } |
| |
| void Writer::unblock() { |
| // Assumes that osl::Condition::set works as a memory barrier, so that |
| // changes made by preceeding sendDirectRequest/Reply calls are visible to |
| // subsequent sendRequest/Reply calls: |
| unblocked_.set(); |
| } |
| |
| void Writer::stop() { |
| { |
| osl::MutexGuard g(mutex_); |
| stop_ = true; |
| } |
| unblocked_.set(); |
| items_.set(); |
| } |
| |
| Writer::~Writer() {} |
| |
| void Writer::run() { |
| setName("binaryurpWriter"); |
| try { |
| unblocked_.wait(); |
| for (;;) { |
| items_.wait(); |
| Item item; |
| { |
| osl::MutexGuard g(mutex_); |
| if (stop_) { |
| return; |
| } |
| OSL_ASSERT(!queue_.empty()); |
| item = queue_.front(); |
| queue_.pop_front(); |
| if (queue_.empty()) { |
| items_.reset(); |
| } |
| } |
| if (item.request) { |
| sendRequest( |
| item.tid, item.oid, item.type, item.member, item.arguments, |
| (!item.oid.equalsAsciiL( |
| RTL_CONSTASCII_STRINGPARAM("UrpProtocolProperties")) && |
| !item.member.equals( |
| css::uno::TypeDescription( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "com.sun.star.uno.XInterface::" |
| "release")))) && |
| bridge_->isCurrentContextMode()), |
| item.currentContext); |
| } else { |
| sendReply( |
| item.tid, item.member, item.setter, item.exception, |
| item.returnValue, item.arguments); |
| if (item.setCurrentContextMode) { |
| bridge_->setCurrentContextMode(); |
| } |
| } |
| } |
| } catch (css::uno::Exception & e) { |
| OSL_TRACE( |
| OSL_LOG_PREFIX "caught UNO 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()); |
| } |
| bridge_->terminate(); |
| } |
| |
| void Writer::onTerminated() { |
| release(); |
| } |
| |
| void Writer::sendRequest( |
| rtl::ByteSequence const & tid, rtl::OUString const & oid, |
| css::uno::TypeDescription const & type, |
| css::uno::TypeDescription const & member, |
| std::vector< BinaryAny > const & inArguments, bool currentContextMode, |
| css::uno::UnoInterfaceReference const & currentContext) |
| { |
| OSL_ASSERT(tid.getLength() != 0 && !oid.isEmpty() && member.is()); |
| css::uno::TypeDescription t(type); |
| sal_Int32 functionId = 0; |
| bool forceSynchronous = false; |
| member.makeComplete(); |
| switch (member.get()->eTypeClass) { |
| case typelib_TypeClass_INTERFACE_ATTRIBUTE: |
| { |
| typelib_InterfaceAttributeTypeDescription * atd = |
| reinterpret_cast< typelib_InterfaceAttributeTypeDescription * >( |
| member.get()); |
| OSL_ASSERT(atd->pInterface != 0); |
| if (!t.is()) { |
| t = css::uno::TypeDescription(&atd->pInterface->aBase); |
| } |
| t.makeComplete(); |
| functionId = atd->pInterface->pMapMemberIndexToFunctionIndex[ |
| atd->aBase.nPosition]; |
| if (!inArguments.empty()) { // setter |
| ++functionId; |
| } |
| break; |
| } |
| case typelib_TypeClass_INTERFACE_METHOD: |
| { |
| typelib_InterfaceMethodTypeDescription * mtd = |
| reinterpret_cast< typelib_InterfaceMethodTypeDescription * >( |
| member.get()); |
| OSL_ASSERT(mtd->pInterface != 0); |
| if (!t.is()) { |
| t = css::uno::TypeDescription(&mtd->pInterface->aBase); |
| } |
| t.makeComplete(); |
| functionId = mtd->pInterface->pMapMemberIndexToFunctionIndex[ |
| mtd->aBase.nPosition]; |
| forceSynchronous = mtd->bOneWay && |
| functionId != SPECIAL_FUNCTION_ID_RELEASE; |
| break; |
| } |
| default: |
| OSL_ASSERT(false); // this cannot happen |
| break; |
| } |
| OSL_ASSERT(functionId >= 0); |
| if (functionId > SAL_MAX_UINT16) { |
| throw css::uno::RuntimeException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM("function ID too large for URP")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| std::vector< unsigned char > buf; |
| bool newType = !(lastType_.is() && t.equals(lastType_)); |
| bool newOid = oid != lastOid_; |
| bool newTid = tid != lastTid_; |
| if (newType || newOid || newTid || forceSynchronous || functionId > 0x3FFF) |
| // > 14 bit function ID |
| { |
| Marshal::write8( |
| &buf, |
| (0xC0 | (newType ? 0x20 : 0) | (newOid ? 0x10 : 0) | |
| (newTid ? 0x08 : 0) | (functionId > 0xFF ? 0x04 : 0) | |
| (forceSynchronous ? 0x01 : 0))); |
| // bit 7: LONGHEADER, bit 6: REQUEST, bit 5: NEWTYPE, bit 4: NEWOID, |
| // bit 3: NEWTID, bit 2: FUNCTIONID16, bit 0: MOREFLAGS |
| if (forceSynchronous) { |
| Marshal::write8(&buf, 0xC0); // bit 7: MUSTREPLY, bit 6: SYNCHRONOUS |
| } |
| if (functionId <= 0xFF) { |
| Marshal::write8(&buf, static_cast< sal_uInt8 >(functionId)); |
| } else { |
| Marshal::write16(&buf, static_cast< sal_uInt16 >(functionId)); |
| } |
| if (newType) { |
| marshal_.writeType(&buf, t); |
| } |
| if (newOid) { |
| marshal_.writeOid(&buf, oid); |
| } |
| if (newTid) { |
| marshal_.writeTid(&buf, tid); |
| } |
| } else if (functionId <= 0x3F) { // <= 6 bit function ID |
| Marshal::write8(&buf, static_cast< sal_uInt8 >(functionId)); |
| // bit 7: !LONGHEADER, bit 6: !FUNCTIONID14 |
| } else { |
| Marshal::write8( |
| &buf, static_cast< sal_uInt8 >(0x40 | (functionId >> 8))); |
| // bit 7: !LONGHEADER, bit 6: FUNCTIONID14 |
| Marshal::write8(&buf, functionId & 0xFF); |
| } |
| if (currentContextMode) { |
| css::uno::UnoInterfaceReference cc(currentContext); |
| marshal_.writeValue( |
| &buf, |
| css::uno::TypeDescription( |
| cppu::UnoType< |
| css::uno::Reference< css::uno::XCurrentContext > >::get()), |
| BinaryAny( |
| css::uno::TypeDescription( |
| cppu::UnoType< |
| css::uno::Reference< |
| css::uno::XCurrentContext > >::get()), |
| &cc.m_pUnoI)); |
| } |
| switch (member.get()->eTypeClass) { |
| case typelib_TypeClass_INTERFACE_ATTRIBUTE: |
| if (!inArguments.empty()) { // setter |
| OSL_ASSERT(inArguments.size() == 1); |
| marshal_.writeValue( |
| &buf, |
| css::uno::TypeDescription( |
| reinterpret_cast< |
| typelib_InterfaceAttributeTypeDescription * >( |
| member.get())-> |
| pAttributeTypeRef), |
| inArguments.front()); |
| } |
| break; |
| case typelib_TypeClass_INTERFACE_METHOD: |
| { |
| typelib_InterfaceMethodTypeDescription * mtd = |
| reinterpret_cast< typelib_InterfaceMethodTypeDescription * >( |
| member.get()); |
| std::vector< BinaryAny >::const_iterator i(inArguments.begin()); |
| for (sal_Int32 j = 0; j != mtd->nParams; ++j) { |
| if (mtd->pParams[j].bIn) { |
| marshal_.writeValue( |
| &buf, |
| css::uno::TypeDescription(mtd->pParams[j].pTypeRef), |
| *i++); |
| } |
| } |
| OSL_ASSERT(i == inArguments.end()); |
| break; |
| } |
| default: |
| OSL_ASSERT(false); // this cannot happen |
| break; |
| } |
| sendMessage(buf); |
| lastType_ = t; |
| lastOid_ = oid; |
| lastTid_ = tid; |
| } |
| |
| void Writer::sendReply( |
| rtl::ByteSequence const & tid, |
| com::sun::star::uno::TypeDescription const & member, bool setter, |
| bool exception, BinaryAny const & returnValue, |
| std::vector< BinaryAny > const & outArguments) |
| { |
| OSL_ASSERT(tid.getLength() != 0 && member.is() && member.get()->bComplete); |
| std::vector< unsigned char > buf; |
| bool newTid = tid != lastTid_; |
| Marshal::write8(&buf, 0x80 | (exception ? 0x20 : 0) | (newTid ? 0x08 : 0)); |
| // bit 7: LONGHEADER; bit 6: !REQUEST; bit 5: EXCEPTION; bit 3: NEWTID |
| if (newTid) { |
| marshal_.writeTid(&buf, tid); |
| } |
| if (exception) { |
| marshal_.writeValue( |
| &buf, |
| css::uno::TypeDescription(cppu::UnoType< css::uno::Any >::get()), |
| returnValue); |
| } else { |
| switch (member.get()->eTypeClass) { |
| case typelib_TypeClass_INTERFACE_ATTRIBUTE: |
| if (!setter) { |
| marshal_.writeValue( |
| &buf, |
| css::uno::TypeDescription( |
| reinterpret_cast< |
| typelib_InterfaceAttributeTypeDescription * >( |
| member.get())-> |
| pAttributeTypeRef), |
| returnValue); |
| } |
| break; |
| case typelib_TypeClass_INTERFACE_METHOD: |
| { |
| typelib_InterfaceMethodTypeDescription * mtd = |
| reinterpret_cast< |
| typelib_InterfaceMethodTypeDescription * >( |
| member.get()); |
| marshal_.writeValue( |
| &buf, css::uno::TypeDescription(mtd->pReturnTypeRef), |
| returnValue); |
| std::vector< BinaryAny >::const_iterator i( |
| outArguments.begin()); |
| for (sal_Int32 j = 0; j != mtd->nParams; ++j) { |
| if (mtd->pParams[j].bOut) { |
| marshal_.writeValue( |
| &buf, |
| css::uno::TypeDescription(mtd->pParams[j].pTypeRef), |
| *i++); |
| } |
| } |
| OSL_ASSERT(i == outArguments.end()); |
| break; |
| } |
| default: |
| OSL_ASSERT(false); // this cannot happen |
| break; |
| } |
| } |
| sendMessage(buf); |
| lastTid_ = tid; |
| bridge_->decrementCalls(); |
| } |
| |
| void Writer::sendMessage(std::vector< unsigned char > const & buffer) { |
| std::vector< unsigned char > header; |
| if (buffer.size() > SAL_MAX_UINT32) { |
| throw css::uno::RuntimeException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM("message too large for URP")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| Marshal::write32(&header, static_cast< sal_uInt32 >(buffer.size())); |
| Marshal::write32(&header, 1); |
| OSL_ASSERT(!buffer.empty()); |
| unsigned char const * p = &buffer[0]; |
| std::vector< unsigned char >::size_type n = buffer.size(); |
| OSL_ASSERT(header.size() <= SAL_MAX_INT32 && SAL_MAX_INT32 <= SAL_MAX_SIZE); |
| sal_Size k = SAL_MAX_INT32 - header.size(); |
| if (n < k) { |
| k = static_cast< sal_Size >(n); |
| } |
| css::uno::Sequence< sal_Int8 > s( |
| static_cast< sal_Int32 >(header.size() + k)); |
| OSL_ASSERT(!header.empty()); |
| rtl_copyMemory( |
| s.getArray(), &header[0], static_cast< sal_Size >(header.size())); |
| for (;;) { |
| rtl_copyMemory(s.getArray() + s.getLength() - k, p, k); |
| try { |
| bridge_->getConnection()->write(s); |
| } catch (css::io::IOException & e) { |
| css::uno::Any exc(cppu::getCaughtException()); |
| throw css::lang::WrappedTargetRuntimeException( |
| (rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "Binary URP write raised IO exception: ")) + |
| e.Message), |
| css::uno::Reference< css::uno::XInterface >(), exc); |
| } |
| n = static_cast< std::vector< unsigned char >::size_type >(n - k); |
| if (n == 0) { |
| break; |
| } |
| p += k; |
| k = SAL_MAX_INT32; |
| if (n < k) { |
| k = static_cast< sal_Size >(n); |
| } |
| s.realloc(k); |
| } |
| } |
| |
| } |