blob: 951f9f084512c0446a8f5030df19e03a6fedf39f [file]
/*
* 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.
*/
#ifndef _IGNITE_IMPL_THIN_DATA_CHANNEL
#define _IGNITE_IMPL_THIN_DATA_CHANNEL
#include <stdint.h>
#include <memory>
#include <ignite/thin/ignite_client_configuration.h>
#include <ignite/common/concurrent.h>
#include <ignite/impl/interop/interop_output_stream.h>
#include <ignite/impl/binary/binary_writer_impl.h>
#include "impl/protocol_version.h"
#include "impl/net/end_point.h"
#include "impl/socket_client.h"
namespace ignite
{
namespace impl
{
namespace interop
{
// Forward declaration.
class InteropMemory;
}
namespace thin
{
/**
* Data router.
*
* Ensures there is a connection between client and one of the servers
* and routes data between them.
*/
class DataChannel
{
public:
/** Version set type. */
typedef std::set<ProtocolVersion> VersionSet;
/** Version 1.2.0. */
static const ProtocolVersion VERSION_1_2_0;
/** Current version. */
static const ProtocolVersion VERSION_CURRENT;
/**
* Operation with timeout result.
*/
struct OperationResult
{
enum T
{
SUCCESS,
FAIL,
TIMEOUT
};
};
/**
* Constructor.
*
* @param cfg Configuration.
* @param typeMgr Type manager.
*/
DataChannel(const ignite::thin::IgniteClientConfiguration& cfg, binary::BinaryTypeManager& typeMgr);
/**
* Destructor.
*/
~DataChannel();
/**
* Establish connection to cluster.
*
* @param host Host.
* @param port Port.
* @param timeout Timeout.
* @return @c true on success.
*/
bool Connect(const std::string& host, uint16_t port, int32_t timeout);
/**
* Close connection.
*/
void Close();
/**
* Synchronously send request message and receive response.
* Uses provided timeout. Does not try to restore connection on
* fail.
*
* @param req Request message.
* @param rsp Response message.
* @param timeout Timeout.
* @throw IgniteError on error.
*/
template<typename ReqT, typename RspT>
void SyncMessage(const ReqT& req, RspT& rsp, int32_t timeout)
{
// Allocating 64KB to lessen number of reallocations.
enum { BUFFER_SIZE = 1024 * 64 };
interop::InteropUnpooledMemory mem(BUFFER_SIZE);
int64_t id = GenerateRequestMessage(req, mem);
InternalSyncMessage(mem, timeout);
interop::InteropInputStream inStream(&mem);
inStream.Position(4);
int64_t rspId = inStream.ReadInt64();
if (id != rspId)
throw IgniteError(IgniteError::IGNITE_ERR_GENERIC,
"Protocol error: Response message ID does not equal Request ID");
binary::BinaryReaderImpl reader(&inStream);
rsp.Read(reader, currentVersion);
}
/**
* Send message stored in memory and synchronously receives
* response and stores it in the same memory.
*
* @param mem Memory.
* @param timeout Opration timeout.
*/
void InternalSyncMessage(interop::InteropUnpooledMemory& mem, int32_t timeout);
/**
* Get address.
* @return Address.
*/
const net::EndPoint& GetAddress() const
{
return address;
}
private:
IGNITE_NO_COPY_ASSIGNMENT(DataChannel);
/**
* Generate request ID.
*
* Atomicaly generates and returns new Request ID.
*
* @return Unique Request ID.
*/
int64_t GenerateRequestId()
{
return common::concurrent::Atomics::IncrementAndGet64(&reqIdCounter);
}
/**
* Generate message to send.
*
* @param req Request to serialize.
* @param mem Memory to write request to.
* @return Message ID.
*/
template<typename ReqT>
int64_t GenerateRequestMessage(const ReqT& req, interop::InteropUnpooledMemory& mem)
{
interop::InteropOutputStream outStream(&mem);
binary::BinaryWriterImpl writer(&outStream, &typeMgr);
// Space for RequestSize + OperationCode + RequestID.
outStream.Reserve(4 + 2 + 8);
req.Write(writer, currentVersion);
int64_t id = GenerateRequestId();
outStream.WriteInt32(0, outStream.Position() - 4);
outStream.WriteInt16(4, ReqT::GetOperationCode());
outStream.WriteInt64(6, id);
outStream.Synchronize();
return id;
}
/**
* Send data by established connection.
*
* @param data Data buffer.
* @param len Data length.
* @param timeout Timeout.
* @return @c true on success, @c false on timeout.
* @throw IgniteError on error.
*/
bool Send(const int8_t* data, size_t len, int32_t timeout);
/**
* Receive next message.
*
* @param msg Buffer for message.
* @param timeout Timeout.
* @return @c true on success, @c false on timeout.
* @throw IgniteError on error.
*/
bool Receive(interop::InteropMemory& msg, int32_t timeout);
/**
* Receive specified number of bytes.
*
* @param dst Buffer for data.
* @param len Number of bytes to receive.
* @param timeout Timeout.
* @return Operation result.
*/
OperationResult::T ReceiveAll(void* dst, size_t len, int32_t timeout);
/**
* Send specified number of bytes.
*
* @param data Data buffer.
* @param len Data length.
* @param timeout Timeout.
* @return Operation result.
*/
OperationResult::T SendAll(const int8_t* data, size_t len, int32_t timeout);
/**
* Perform handshake request.
*
* @param propVer Proposed protocol version.
* @param resVer Resulted version.
* @param timeout Timeout.
* @return @c true on success and @c false otherwise.
*/
bool MakeRequestHandshake(const ProtocolVersion& propVer, ProtocolVersion& resVer, int32_t timeout);
/**
* Synchronously send handshake request message and receive
* handshake response. Uses provided timeout. Does not try to
* restore connection on fail.
*
* @param propVer Proposed protocol version.
* @param resVer Resulted version.
* @param timeout Timeout.
* @return @c true if accepted.
* @throw IgniteError on error.
*/
bool Handshake(const ProtocolVersion& propVer, ProtocolVersion& resVer, int32_t timeout);
/**
* Ensure there is a connection to the cluster.
*
* @param timeout Timeout.
* @return @c false on error.
*/
bool EnsureConnected(int32_t timeout);
/**
* Negotiate protocol version with current host
*
* @param timeout Timeout.
* @return @c true on success and @c false otherwise.
*/
bool NegotiateProtocolVersion(int32_t timeout);
/**
* Try to restore connection to the cluster.
*
* @param timeout Timeout.
* @return @c true on success and @c false otherwise.
*/
bool TryRestoreConnection(int32_t timeout);
/**
* Check if the version is supported.
*
* @param ver Version.
* @return True if the version is supported.
*/
static bool IsVersionSupported(const ProtocolVersion& ver);
/** Set of supported versions. */
const static VersionSet supportedVersions;
/** Sync IO mutex. */
common::concurrent::CriticalSection ioMutex;
/** Remote host address. */
net::EndPoint address;
/** Configuration. */
const ignite::thin::IgniteClientConfiguration& config;
/** Metadata manager. */
binary::BinaryTypeManager& typeMgr;
/** Protocol version. */
ProtocolVersion currentVersion;
/** Request ID counter. */
int64_t reqIdCounter;
/** Client Socket. */
std::auto_ptr<SocketClient> socket;
};
/** Shared pointer type. */
typedef common::concurrent::SharedPointer<DataChannel> SP_DataChannel;
}
}
}
#endif //_IGNITE_IMPL_THIN_DATA_CHANNEL