MQTTSuite
Loading...
Searching...
No Matches
BridgeStore.cpp
Go to the documentation of this file.
1/*
2 * MQTTSuite - A lightweight MQTT Integration System
3 * Copyright (C) Volker Christian <me@vchrist.at>
4 * 2022, 2023, 2024, 2025, 2026
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation, either version 3 of the License, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20/*
21 * MIT License
22 *
23 * Permission is hereby granted, free of charge, to any person obtaining a copy
24 * of this software and associated documentation files (the "Software"), to deal
25 * in the Software without restriction, including without limitation the rights
26 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
27 * copies of the Software, and to permit persons to whom the Software is
28 * furnished to do so, subject to the following conditions:
29 *
30 * The above copyright notice and this permission notice shall be included in
31 * all copies or substantial portions of the Software.
32 *
33 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
39 * THE SOFTWARE.
40 */
41
42#include "BridgeStore.h"
43
44#include "lib/SSEDistributor.h"
45
46#ifndef DOXYGEN_SHOULD_SKIP_THIS
47
48#include <cmath>
49#include <exception>
50#include <fstream>
51#include <list>
52#include <log/Logger.h>
53#include <map>
54#include <utility>
55
56// IWYU pragma: no_include <nlohmann/json_fwd.hpp>
57// IWYU pragma: no_include <nlohmann/detail/iterators/iter_impl.hpp>
58
59#endif // DOXYGEN_SHOULD_SKIP_THIS
60
61namespace mqtt::bridge::lib {
62
63#include "bridge-schema.json.h" // IWYU pragma: keep
64
66 static BridgeStore bridgeConfigLoader;
67
68 return bridgeConfigLoader;
69 }
70
71 bool BridgeStore::loadAndValidate(const std::string& fileName) {
72 this->fileName = fileName;
73
74 bool success = bridgeMap.empty();
75
76 if (success) {
77 try {
78 const nlohmann::json bridgeJsonSchema = nlohmann::json::parse(bridgeJsonSchemaString);
79 validator.set_root_schema(bridgeJsonSchema);
80
81 if (!fileName.empty()) {
82 std::ifstream bridgeConfigJsonFile(fileName);
83
84 if (bridgeConfigJsonFile.is_open()) {
85 VLOG(1) << "Bridge config JSON: " << fileName;
86
87 try {
88 bridgeConfigJsonFile >> bridgesConfigJsonStaged;
89
90 try {
91 try {
93
94 try {
96
98
99 success = true;
100 } catch (const std::exception& e) {
101 VLOG(1) << " Patching JSON with default patch failed:\n" << defaultPatch.dump(4);
102 VLOG(1) << " " << e.what();
103 }
104 } catch (const std::exception& e) {
105 VLOG(1) << " Validating JSON failed:\n" << bridgesConfigJsonActive.dump(4);
106 VLOG(1) << " " << e.what();
107 }
108 } catch (const std::exception& e) {
109 VLOG(1) << " Setting root json mapping schema failed:\n" << bridgeJsonSchema.dump(4);
110 VLOG(1) << " " << e.what();
111 }
112 } catch (const std::exception& e) {
113 VLOG(1) << " JSON map file parsing failed:" << e.what() << " at " << bridgeConfigJsonFile.tellg();
114 }
115
116 bridgeConfigJsonFile.close();
117 } else {
118 VLOG(1) << "BridgeJsonConfig: " << fileName << " not found";
119 }
120 } else {
121 // Do not log missing path. In regular use this missing option is captured by the command line interface
122 }
123 } catch (const std::exception& e) {
124 VLOG(1) << "Parsing schema failed: " << e.what();
125 VLOG(1) << bridgeJsonSchemaString;
126 }
127 } else {
128 VLOG(1) << "MappingFile already loaded and validated";
129 }
130
131 return success;
132 }
133
134 bool BridgeStore::patch(const nlohmann::json& jsonPatch) {
135 bool success = false;
136
137 try {
139
140 try {
141 const nlohmann::json defaultPatch = validator.validate(bridgesConfigJsonStaged);
142
143 try {
145
146 success = true;
147 } catch (const std::exception& e) {
148 VLOG(1) << " Patching JSON with default patch failed:\n" << defaultPatch.dump(4);
149 VLOG(1) << " " << e.what();
150 }
151 } catch (const std::exception& e) {
152 VLOG(1) << " Validating JSON failed:\n" << bridgesConfigJsonActive.dump(4);
153 VLOG(1) << " " << e.what();
154 }
155 } catch (const std::exception& e) {
156 VLOG(1) << " Default Patch:\n" << bridgesConfigJsonActive.dump(4);
157
158 VLOG(1) << " Patching JSON with update failed:\n" << jsonPatch.dump(4);
159 VLOG(1) << " " << e.what();
160 }
161
162 return success;
163 }
164
166 for (auto& [fullInstanceName, bridge] : bridgeMap) {
167 bridge.clear();
168 }
169
170 bridgeMap.clear();
171
173
174 std::ofstream ofs(fileName, std::ios::binary);
175
176 ofs << bridgesConfigJsonActive.dump(4);
177
178 ofs.close();
179
180 for (const nlohmann::json& bridgeConfigJson : bridgesConfigJsonActive["bridges"]) {
181 bridgeMap.emplace(bridgeConfigJson["name"],
182 Bridge{bridgeConfigJson["name"], bridgeConfigJson["prefix"], bridgeConfigJson["disabled"]});
183
184 for (const nlohmann::json& brokerConfigJson : bridgeConfigJson["brokers"]) {
185 std::list<iot::mqtt::Topic> topics;
186 for (const nlohmann::json& topicJson : brokerConfigJson["topics"]) {
187 if (!topicJson["topic"].get<std::string>().empty()) {
188 topics.emplace_back(topicJson["topic"], // cppcheck-suppress useStlAlgorithm
189 topicJson["qos"]);
190 }
191 }
192
193 const nlohmann::json& mqtt = brokerConfigJson["mqtt"];
194 const nlohmann::json& network = brokerConfigJson["network"];
195
196 const std::string fullInstanceName =
197 bridgeMap.find(bridgeConfigJson["name"])->second.getName() + "+" + network["instance_name"].get<std::string>();
198
199 bridgeMap.find(bridgeConfigJson["name"])
200 ->second.addBroker(fullInstanceName,
201 Broker(bridgeMap.find(bridgeConfigJson["name"])->second,
202 brokerConfigJson["session_store"],
203 fullInstanceName,
204 network["protocol"],
205 network["encryption"],
206 network["transport"],
207 network[network["protocol"]],
208 mqtt["client_id"],
209 mqtt["keep_alive"],
210 mqtt["clean_session"],
211 mqtt["will_topic"],
212 mqtt["will_message"],
213 mqtt["will_qos"],
214 mqtt["will_retain"],
215 mqtt["username"],
216 mqtt["password"],
217 mqtt["loop_prevention"],
218 brokerConfigJson["prefix"],
219 brokerConfigJson["disabled"],
220 topics));
221 }
222 }
223 }
224
226 return bridgeMap;
227 }
228
232
233 void BridgeStore::mqttConnected(Broker& broker, Mqtt* mqtt) const {
234 broker.getBridge().addMqtt(mqtt);
235
236 bool allConnected = true;
237
238 for (const auto& [bridgeName, bridge] : bridgeMap) {
239 allConnected &= bridge.getAllConnected1();
240 }
241
242 if (allConnected) {
244 }
245 }
246
247 void BridgeStore::mqttDisconnected(Broker& broker, Mqtt* mqtt) const {
249 }
250
251 static std::pair<std::string, std::string> split_plus(const std::string& s) {
252 const auto pos = s.find('+');
253 if (pos == std::string::npos)
254 return {s, {}};
255 return {s.substr(0, pos), s.substr(pos + 1)};
256 };
257
258 Broker* BridgeStore::getBroker(const std::string& fullInstanceName) {
259 auto [bridgeName, instanceName] = split_plus(fullInstanceName);
260
261 return bridgeMap.find(bridgeName)->second.getBroker(fullInstanceName);
262 }
263
264} // namespace mqtt::bridge::lib
nlohmann::json bridgesConfigJsonActive
Definition BridgeStore.h:90
std::map< const std::string, Bridge > bridgeMap
Definition BridgeStore.h:88
void mqttConnected(Broker &broker, mqtt::bridge::lib::Mqtt *mqtt) const
Broker * getBroker(const std::string &fullInstanceName)
const std::map< const std::string, Bridge > & getBridgeMap()
static BridgeStore & instance()
void mqttDisconnected(Broker &broker, mqtt::bridge::lib::Mqtt *mqtt) const
nlohmann::json_schema::json_validator validator
Definition BridgeStore.h:92
nlohmann::json bridgesConfigJsonStaged
Definition BridgeStore.h:91
bool loadAndValidate(const std::string &fileName)
const nlohmann::json & getBridgesConfigJson()
bool patch(const nlohmann::json &jsonPatch)
void addMqtt(mqtt::bridge::lib::Mqtt *mqtt)
Definition Bridge.cpp:85
void removeMqtt(mqtt::bridge::lib::Mqtt *mqtt)
Definition Bridge.cpp:96
Bridge & getBridge() const
Definition Broker.cpp:94
static SSEDistributor & instance()
static std::pair< std::string, std::string > split_plus(const std::string &s)