blob: f024e8ed3bf7fd1d8dcb7037661abb6abe8f96c8 [file] [log] [blame]
/**************************************************************
*
* 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);
}
}
}