blob: c90a39dc770efed69024c882d4b4369943e65f6d [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.
*/
// This needs to be included first to let uuid.h sort out the system header collisions
#ifndef WIN32
#include "uuid++.hh"
#endif
#include "utils/Id.h"
#define __STDC_FORMAT_MACROS 1
#include <inttypes.h>
#include <algorithm>
#include <chrono>
#include <cmath>
#include <cstdio>
#include <memory>
#include <string>
#include <limits>
#include "core/logging/LoggerConfiguration.h"
#include "utils/StringUtils.h"
#ifdef WIN32
#include "Rpc.h"
#include "Winsock2.h"
#pragma comment(lib, "Rpcrt4.lib")
#pragma comment(lib, "Ws2_32.lib")
#endif
namespace org {
namespace apache {
namespace nifi {
namespace minifi {
namespace utils {
#ifdef WIN32
namespace {
void windowsUuidToUuidField(UUID* uuid, UUID_FIELD out) {
uint32_t Data1BE = htonl(uuid->Data1);
memcpy(out, &Data1BE, 4);
uint16_t Data2BE = htons(uuid->Data2);
memcpy(out + 4, &Data2BE, 2);
uint16_t Data3BE = htons(uuid->Data3);
memcpy(out + 6, &Data3BE, 2);
memcpy(out + 8, uuid->Data4, 8);
}
void windowsUuidGenerateTime(UUID_FIELD out) {
UUID uuid;
UuidCreateSequential(&uuid);
windowsUuidToUuidField(&uuid, out);
}
void windowsUuidGenerateRandom(UUID_FIELD out) {
UUID uuid;
UuidCreate(&uuid);
windowsUuidToUuidField(&uuid, out);
}
} // namespace
#endif
Identifier::Identifier(UUID_FIELD u)
: IdentifierBase(u) {
build_string();
}
Identifier::Identifier()
: IdentifierBase() {
}
Identifier::Identifier(const Identifier &other) {
if (!other.convert().empty()) {
copyInto(other);
build_string();
}
}
Identifier::Identifier(Identifier &&other)
: IdentifierBase(std::move(other)) {
}
Identifier::Identifier(const IdentifierBase &other) {
if (!other.convert().empty()) {
copyInto(other);
build_string();
}
}
Identifier &Identifier::operator=(const Identifier &other) {
if (!other.convert().empty()) {
IdentifierBase::operator =(other);
build_string();
}
return *this;
}
Identifier &Identifier::operator=(const IdentifierBase &other) {
if (!other.convert().empty()) {
IdentifierBase::operator =(other);
build_string();
}
return *this;
}
Identifier &Identifier::operator=(UUID_FIELD o) {
IdentifierBase::operator=(o);
build_string();
return *this;
}
Identifier &Identifier::operator=(std::string id) {
sscanf(id.c_str(), "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
&id_[0], &id_[1], &id_[2], &id_[3],
&id_[4], &id_[5],
&id_[6], &id_[7],
&id_[8], &id_[9],
&id_[10], &id_[11], &id_[12], &id_[13], &id_[14], &id_[15]);
build_string();
return *this;
}
bool Identifier::operator==(const std::nullptr_t nullp) const {
return converted_.empty();
}
bool Identifier::operator!=(const std::nullptr_t nullp) const {
return !converted_.empty();
}
bool Identifier::operator!=(const Identifier &other) const {
return converted_ != other.converted_;
}
bool Identifier::operator==(const Identifier &other) const {
return converted_ == other.converted_;
}
std::string Identifier::to_string() const {
return convert();
}
const unsigned char * const Identifier::toArray() const {
return id_;
}
void Identifier::build_string() {
char uuidStr[37];
snprintf(uuidStr, sizeof(uuidStr), "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
id_[0], id_[1], id_[2], id_[3],
id_[4], id_[5],
id_[6], id_[7],
id_[8], id_[9],
id_[10], id_[11], id_[12], id_[13], id_[14], id_[15]);
converted_ = uuidStr;
}
IdGenerator::IdGenerator()
: implementation_(UUID_TIME_IMPL),
logger_(logging::LoggerFactory<IdGenerator>::getLogger()),
incrementor_(0) {
#ifndef WIN32
uuid_impl_ = std::unique_ptr<uuid>(new uuid());
#endif
}
IdGenerator::~IdGenerator() = default;
uint64_t IdGenerator::getDeviceSegmentFromString(const std::string& str, int numBits) const {
uint64_t deviceSegment = 0;
for (size_t i = 0; i < str.length(); i++) {
unsigned char c = toupper(str[i]);
if (c >= '0' && c <= '9') {
deviceSegment = deviceSegment + (c - '0');
} else if (c >= 'A' && c <= 'F') {
deviceSegment = deviceSegment + (c - 'A' + 10);
} else {
logging::LOG_ERROR(logger_) << "Expected hex char (0-9, A-F). Got " << c;
}
deviceSegment = deviceSegment << 4;
}
deviceSegment <<= 64 - (4 * (str.length() + 1));
deviceSegment >>= 64 - numBits;
logging::LOG_DEBUG(logger_) << "Using user defined device segment: " << std::hex << deviceSegment;
deviceSegment <<= 64 - numBits;
return deviceSegment;
}
uint64_t IdGenerator::getRandomDeviceSegment(int numBits) const {
uint64_t deviceSegment = 0;
UUID_FIELD random_uuid;
for (int word = 0; word < 2; word++) {
#ifdef WIN32
windowsUuidGenerateRandom(random_uuid);
#else
uuid temp_uuid;
temp_uuid.make(UUID_MAKE_V4);
void* uuid_bin = temp_uuid.binary();
memcpy(random_uuid, uuid_bin, 16);
free(uuid_bin);
#endif
for (int i = 0; i < 4; i++) {
deviceSegment += random_uuid[i];
deviceSegment <<= 8;
}
}
deviceSegment >>= 64 - numBits;
logging::LOG_DEBUG(logger_) << "Using random defined device segment:" << deviceSegment;
deviceSegment <<= 64 - numBits;
return deviceSegment;
}
void IdGenerator::initialize(const std::shared_ptr<Properties> & properties) {
std::string implementation_str;
implementation_ = UUID_TIME_IMPL;
if (properties->get("uid.implementation", implementation_str)) {
std::transform(implementation_str.begin(), implementation_str.end(), implementation_str.begin(), ::tolower);
if (UUID_RANDOM_STR == implementation_str || UUID_WINDOWS_RANDOM_STR == implementation_str) {
logging::LOG_DEBUG(logger_) << "Using uuid_generate_random for uids.";
implementation_ = UUID_RANDOM_IMPL;
} else if (UUID_DEFAULT_STR == implementation_str) {
logging::LOG_DEBUG(logger_) << "Using uuid_generate for uids.";
implementation_ = UUID_DEFAULT_IMPL;
} else if (MINIFI_UID_STR == implementation_str) {
logging::LOG_DEBUG(logger_) << "Using minifi uid implementation for uids";
implementation_ = MINIFI_UID_IMPL;
uint64_t timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
int device_bits = properties->getInt("uid.minifi.device.segment.bits", 16);
std::string device_segment;
uint64_t prefix = timestamp;
if (device_bits > 0) {
if (properties->get("uid.minifi.device.segment", device_segment)) {
prefix = getDeviceSegmentFromString(device_segment, device_bits);
} else {
logging::LOG_WARN(logger_) << "uid.minifi.device.segment not specified, generating random device segment";
prefix = getRandomDeviceSegment(device_bits);
}
timestamp <<= device_bits;
timestamp >>= device_bits;
prefix = prefix + timestamp;
logging::LOG_DEBUG(logger_) << "Using minifi uid prefix: " << std::hex << prefix;
}
for (int i = 0; i < 8; i++) {
unsigned char prefix_element = (prefix >> ((7 - i) * 8)) & std::numeric_limits<unsigned char>::max();
deterministic_prefix_[i] = prefix_element;
}
incrementor_ = 0;
} else if (UUID_TIME_STR == implementation_str || UUID_WINDOWS_STR == implementation_str) {
logging::LOG_DEBUG(logger_) << "Using uuid_generate_time implementation for uids.";
} else {
logging::LOG_DEBUG(logger_) << "Invalid value for uid.implementation (" << implementation_str << "). Using uuid_generate_time implementation for uids.";
}
} else {
logging::LOG_DEBUG(logger_) << "Using uuid_generate_time implementation for uids.";
}
}
#ifndef WIN32
bool IdGenerator::generateWithUuidImpl(unsigned int mode, UUID_FIELD output) {
void* uuid = nullptr;
try {
std::lock_guard<std::mutex> lock(uuid_mutex_);
uuid_impl_->make(mode);
uuid = uuid_impl_->binary();
} catch (uuid_error_t& uuid_error) {
logger_->log_error("Failed to generate UUID, error: %s", uuid_error.string());
return false;
}
memcpy(output, uuid, 16);
free(uuid);
return true;
}
#endif
Identifier IdGenerator::generate() {
Identifier ident;
generate(ident);
return ident;
}
void IdGenerator::generate(Identifier &ident) {
UUID_FIELD output;
switch (implementation_) {
case UUID_RANDOM_IMPL:
case UUID_DEFAULT_IMPL:
#ifdef WIN32
windowsUuidGenerateRandom(output);
#else
generateWithUuidImpl(UUID_MAKE_V4, output);
#endif
break;
case MINIFI_UID_IMPL: {
std::memcpy(output, deterministic_prefix_, sizeof(deterministic_prefix_));
uint64_t incrementor_value = incrementor_++;
for (int i = 8; i < 16; i++) {
output[i] = (incrementor_value >> ((15 - i) * 8)) & std::numeric_limits<unsigned char>::max();
}
}
break;
case UUID_TIME_IMPL:
default:
#ifdef WIN32
windowsUuidGenerateTime(output);
#else
generateWithUuidImpl(UUID_MAKE_V1, output);
#endif
break;
}
ident = output;
}
} /* namespace utils */
} /* namespace minifi */
} /* namespace nifi */
} /* namespace apache */
} /* namespace org */