1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#include "listener/listener.h"
6#include "listener/private/listener_p.h"
7#include "pluginmanager_p.h"
8#include "lifecycle/private/pluginmetaobject_p.h"
9#include "lifecycle/plugin.h"
10#include "lifecycle/pluginsetting.h"
11
12#include "common/util/custompaths.h"
13#include "log/frameworklog.h"
14
15DPF_BEGIN_NAMESPACE
16
17namespace GlobalPrivate {
18 static QMutex mutex;
19} // namespace GlobalPrivate
20
21PluginManagerPrivate::PluginManagerPrivate(PluginManager *qq)
22 : q(qq)
23{
24 dpfCheckTimeBegin();
25 dpfCheckTimeEnd();
26}
27
28PluginManagerPrivate::~PluginManagerPrivate()
29{
30 stopPlugins();
31}
32
33QString PluginManagerPrivate::pluginIID() const
34{
35 return pluginLoadIID;
36}
37
38void PluginManagerPrivate::setPluginIID(const QString &pluginIID)
39{
40 pluginLoadIID = pluginIID;
41}
42
43QStringList PluginManagerPrivate::pluginPaths() const
44{
45 return pluginLoadPaths;
46}
47
48void PluginManagerPrivate::setPluginPaths(const QStringList &pluginPaths)
49{
50 pluginLoadPaths = pluginPaths;
51 readSettings();
52}
53
54QStringList PluginManagerPrivate::servicePaths() const
55{
56 return serviceLoadPaths;
57}
58
59void PluginManagerPrivate::setServicePaths(const QStringList &servicePaths)
60{
61 serviceLoadPaths = servicePaths;
62}
63
64void PluginManagerPrivate::setPluginEnable(const PluginMetaObject &meta, bool enabled)
65{
66 return setting->setPluginEnable(meta,enabled);
67}
68
69/*!
70 * \brief 获取插件的元数据,线程安全
71 * \param name
72 * \param version
73 * \return
74 */
75PluginMetaObjectPointer PluginManagerPrivate::pluginMetaObj(const QString &name,
76 const QString &version)
77{
78 dpfCheckTimeBegin();
79
80 auto controller = QtConcurrent::run([=](){
81 QMutexLocker lock(&GlobalPrivate::mutex);
82 int size = readQueue.size();
83 int idx = 0;
84 while (idx < size) {
85 if (!version.isEmpty()) {
86 if (readQueue[idx]->d->version == version
87 && readQueue[idx]->d->name == name) {
88 return readQueue[idx];
89 }
90 } else {
91 if (readQueue[idx]->d->name == name) {
92 return readQueue[idx];
93 }
94 }
95 idx++;
96 }
97 return PluginMetaObjectPointer(nullptr);
98 });
99 controller.waitForFinished();
100
101 dpfCheckTimeBegin();
102
103 return controller.result();
104}
105
106/*!
107 * \brief 加载一个插件,线程安全,可单独使用
108 * \param pluginMetaObj
109 */
110bool PluginManagerPrivate::loadPlugin(PluginMetaObjectPointer &pluginMetaObj)
111{
112 dpfCheckTimeBegin();
113 // don't load disabled plugins
114 if (!pluginMetaObj->isEnabledBySettings())
115 return true;
116
117 QMutexLocker lock(&GlobalPrivate::mutex);
118
119 //流程互斥
120 if (pluginMetaObj->d->state >= PluginMetaObject::State::Loaded) {
121 dpfDebug() << "Plugin"
122 << pluginMetaObj->d->name
123 << "already loaded and state: "
124 << pluginMetaObj->d->state;
125 return true;
126 }
127
128 if (pluginMetaObj.isNull()) {
129 dpfCritical() << "Failed, load plugin is nullptr";
130 return false;
131 }
132
133 bool result = pluginMetaObj->d->loader->load();
134 pluginMetaObj->d->plugin = QSharedPointer<Plugin>(
135 qobject_cast<Plugin*>(pluginMetaObj->d->loader->instance()));
136 if (!pluginMetaObj->d->plugin.isNull()) {
137 pluginMetaObj->d->state = PluginMetaObject::State::Loaded;
138 dpfDebug() << "Loaded plugin: " << pluginMetaObj->d->name;
139 } else {
140 pluginMetaObj->d->error = "Failed, get plugin instance is nullptr: "
141 + pluginMetaObj->d->loader->errorString();
142 dpfCritical() << pluginMetaObj->d->error;
143 result = false;
144 }
145
146 dpfCheckTimeEnd();
147
148 return result;
149}
150
151/*!
152 * \brief 初始化一个插件,线程安全,可单独使用
153 * \param pluginMetaObj
154 */
155bool PluginManagerPrivate::initPlugin(PluginMetaObjectPointer &pluginMetaObj)
156{
157 dpfCheckTimeBegin();
158
159 //流程互斥
160 if (pluginMetaObj->d->state >= PluginMetaObject::State::Initialized) {
161 dpfCritical() << "Plugin"
162 << pluginMetaObj->d->name
163 << "already initialized and state: "
164 << pluginMetaObj->d->state;
165 return true;
166 }
167
168 if (pluginMetaObj.isNull()) {
169 dpfCritical() << "Failed, init plugin is nullptr";
170 return false;
171 }
172
173 auto pluginInterface = pluginMetaObj->plugin();
174
175 if (pluginInterface.isNull()) {
176 dpfCritical() << "Failed, init plugin interface is nullptr";
177 return false;
178 }
179
180 //线程互斥
181 QMutexLocker lock(&GlobalPrivate::mutex);
182
183 pluginMetaObj->d->plugin->initialize();
184 dpfDebug() << "Initialized plugin: " << pluginMetaObj->d->name;
185 pluginMetaObj->d->state = PluginMetaObject::State::Initialized;
186
187 dpfCheckTimeEnd();
188 return true;
189}
190
191/*!
192 * \brief 启动一个插件,线程安全,可单独使用
193 * \param pluginMetaObj
194 */
195bool PluginManagerPrivate::startPlugin(PluginMetaObjectPointer &pluginMetaObj)
196{
197 dpfCheckTimeBegin();
198
199 //流程互斥
200 if (pluginMetaObj->d->state >= PluginMetaObject::State::Started) {
201 dpfCritical() << "State error: " << pluginMetaObj->d->state;
202 return false;
203 }
204
205 if (pluginMetaObj.isNull()) {
206 dpfCritical() << "Failed, start plugin is nullptr";
207 return false;
208 }
209
210 auto pluginInterface = pluginMetaObj->plugin();
211
212 if (pluginInterface.isNull()) {
213 dpfCritical() << "Failed, start plugin interface is nullptr";
214 return false;
215 }
216
217 //线程互斥
218 QMutexLocker lock(&GlobalPrivate::mutex);
219
220 if (pluginMetaObj->d->plugin->start()) {
221 pluginMetaObj->d->state = PluginMetaObject::State::Started;
222 dpfDebug() << "Started plugin: " << pluginMetaObj->d->name;
223 } else {
224 pluginMetaObj->d->error = "Failed, start plugin in function start() logic";
225 dpfCritical() << pluginMetaObj->d->error.toLocal8Bit().data();
226 }
227
228 dpfCheckTimeEnd();
229 return true;
230}
231
232/*!
233 * \brief 停止并卸载一个插件,线程安全,可单独使用
234 * \param pluginMetaObj
235 */
236void PluginManagerPrivate::stopPlugin(PluginMetaObjectPointer &pluginMetaObj)
237{
238 dpfCheckTimeBegin();
239
240 //流程互斥
241 if (pluginMetaObj->d->state >= PluginMetaObject::State::Stoped)
242 return;
243
244 //线程互斥
245 QMutexLocker lock(&GlobalPrivate::mutex);
246
247 Plugin::ShutdownFlag stFlag = pluginMetaObj->d->plugin->stop();
248 pluginMetaObj->d->state = PluginMetaObject::State::Stoped;
249
250 if (stFlag == Plugin::ShutdownFlag::Async) {
251
252 dpfDebug() << "async stop" << pluginMetaObj->d->plugin->
253 metaObject()->className();
254
255 pluginMetaObj->d->state = PluginMetaObject::State::Stoped;
256
257 QObject::connect(pluginMetaObj->d->plugin.data(), &Plugin::asyncStopFinished,
258 pluginMetaObj->d->plugin.data(), [=]() {
259 pluginMetaObj->d->plugin = nullptr;
260
261 if (!pluginMetaObj->d->loader->unload()) {
262 dpfDebug() << pluginMetaObj->d->loader->errorString();
263 }
264
265 pluginMetaObj->d->state = PluginMetaObject::State::Shutdown;
266 dpfDebug() << "shutdown" << pluginMetaObj->d->loader->fileName();
267
268 },Qt::ConnectionType::DirectConnection); //同步信号直接调用无需等待
269
270 } else {
271
272 if (pluginMetaObj->d->plugin) {
273 dpfDebug() << "sync stop" << pluginMetaObj->d->plugin->
274 metaObject()->className();
275
276 pluginMetaObj->d->plugin = nullptr;
277 pluginMetaObj->d->state = PluginMetaObject::State::Stoped;
278 }
279
280 if (!pluginMetaObj->d->loader->unload()) {
281 dpfDebug() << pluginMetaObj->d->loader->errorString();
282 }
283
284 pluginMetaObj->d->state = PluginMetaObject::State::Shutdown;
285 dpfDebug() << "shutdown" << pluginMetaObj->d->loader->fileName();
286 }
287
288 dpfCheckTimeEnd();
289}
290
291/*!
292 * \brief 读取所有插件的Json源数据
293 * \return 如果未读到任何插件则false
294 */
295bool PluginManagerPrivate::readPlugins()
296{
297 dpfCheckTimeBegin();
298
299 // 内部已有线程互斥
300 scanfAllPlugin(readQueue, pluginLoadPaths, pluginLoadIID);
301
302 QMutexLocker lock(&GlobalPrivate::mutex);
303 int currMaxCountThread = QThreadPool::globalInstance()->maxThreadCount();
304 if (currMaxCountThread < 4) {
305 QThreadPool::globalInstance()->setMaxThreadCount(4);
306 }
307
308 // 并发读取数据
309 QFuture<void> mapController = QtConcurrent::map(readQueue.begin(),
310 readQueue.end(),
311 readJsonToMeta);
312 mapController.waitForFinished();
313 dpfDebug() << readQueue;
314
315 dpfCheckTimeEnd();
316 if (!readQueue.isEmpty()) {
317 foreach (PluginMetaObjectPointer pluginPointer, readQueue) {
318 if (disabledPlugins.contains(pluginPointer->name())) {
319 pluginPointer->setEnabledBySettings(false);
320 } else if (enabledPlugins.contains(pluginPointer->name())) {
321 pluginPointer->setEnabledBySettings(true);
322 } else {
323 setting->setValue(QLatin1String(ENABLED_PLUGINS), pluginPointer->name());
324 }
325 pluginCategories[pluginPointer->category()].append(pluginPointer);
326 }
327 return true;
328 }
329 return false;
330}
331
332/*!
333 * \brief 扫描所有插件到目标队列
334 * \param destQueue 目标队列
335 * \param pluginPaths 插件路径列表
336 * \param pluginIID 插件身份
337 */
338void PluginManagerPrivate::scanfAllPlugin(PluginMetaQueue &destQueue,
339 const QStringList &pluginPaths,
340 const QString &pluginIID)
341{
342 dpfCheckTimeBegin();
343
344 if (pluginIID.isEmpty())
345 return;
346
347 for(const QString &path : pluginPaths) {
348 QDirIterator dirItera(path, {"*.so", "*.dll"},
349 QDir::Filter::Files,
350 QDirIterator::IteratorFlag::NoIteratorFlags);
351
352 //线程安全
353 QMutexLocker lock(&GlobalPrivate::mutex);
354
355 while (dirItera.hasNext()) {
356 dirItera.next();
357
358 PluginMetaObjectPointer metaObj(new PluginMetaObject);
359 metaObj->d->loader->setFileName(dirItera.path() + QDir::separator() + dirItera.fileName());
360 QJsonObject &&metaJson = metaObj->d->loader->metaData();
361 QString &&IID = metaJson.value("IID").toString();
362 if (pluginIID != IID) {
363 continue;
364 }
365
366 destQueue.append(metaObj);
367 metaObj->d->state = PluginMetaObject::Readed;
368 }
369 }
370
371 dpfCheckTimeEnd();
372}
373
374/*!
375 * \brief 同步json到定义类型
376 * \param metaObject
377 */
378void PluginManagerPrivate::readJsonToMeta(const PluginMetaObjectPointer &metaObject)
379{
380 dpfCheckTimeBegin();
381
382 metaObject->d->state = PluginMetaObject::Reading;
383
384 QJsonObject &&jsonObj = metaObject->d->loader->metaData();
385
386 if (jsonObj.isEmpty()) return;
387
388 QString &&iid = jsonObj.value("IID").toString();
389 if (iid.isEmpty()) return;
390 metaObject->d->iid = iid;
391
392 QJsonObject &&metaData = jsonObj.value("MetaData").toObject();
393
394 QString &&name = metaData.value(PLUGIN_NAME).toString();
395 if (name.isEmpty()) return;
396 metaObject->d->name = name;
397
398 QString &&version = metaData.value(PLUGIN_VERSION).toString();
399 metaObject->d->version = version;
400
401 QString &&compatVersion = metaData.value(PLUGIN_COMPATVERSION).toString();
402 metaObject->d->compatVersion = compatVersion;
403
404 QString &&category = metaData.value(PLUGIN_CATEGORY).toString();
405 metaObject->d->category = category;
406
407 QJsonArray &&licenseArray = metaData.value(PLUGIN_LICENSE).toArray();
408 auto licenItera = licenseArray.begin();
409 while (licenItera != licenseArray.end()) {
410 metaObject->d->license.append(licenItera->toString());
411 ++ licenItera;
412 }
413
414 QString &&copyright = metaData.value(PLUGIN_COPYRIGHT).toString();
415 metaObject->d->copyright = copyright;
416
417 QString &&vendor = metaData.value(PLUGIN_VENDOR).toString();
418 metaObject->d->vendor = vendor;
419
420 QString &&description = metaData.value(PLUGIN_DESCRIPTION).toString();
421 metaObject->d->description = description;
422
423 QString &&urlLink = metaData.value(PLUGIN_URLLINK).toString();
424 metaObject->d->urlLink = urlLink;
425
426 QJsonArray &&dependsArray = metaData.value(PLUGIN_DEPENDS).toArray();
427 auto itera = dependsArray.begin();
428 while (itera != dependsArray.end()) {
429 QJsonObject &&dependObj = itera->toObject();
430 QString &&dependName = dependObj.value(PLUGIN_NAME).toString();
431 QString &&dependVersion = dependObj.value(PLUGIN_VERSION).toString();
432 PluginDepend depends;
433 depends.pluginName = dependName;
434 depends.pluginVersion = dependVersion;
435 metaObject->d->depends.append(depends);
436 ++ itera;
437 }
438
439 metaObject->d->state = PluginMetaObject::Readed;
440
441 dpfCheckTimeEnd();
442}
443
444QHash<QString, QQueue<PluginMetaObjectPointer> > PluginManagerPrivate::categories()
445{
446 return pluginCategories;
447}
448
449/*!
450 * \brief PluginManagerPrivate::dependsSort 插件依赖排序算法
451 * \param srcQueue 传入需要排序的插件源队列
452 * \return 返回排序后的Queue
453 */
454PluginManagerPrivate::PluginMetaQueue PluginManagerPrivate::dependsSort(
455 const PluginManagerPrivate::PluginMetaQueue &srcQueue)
456{
457 dpfCheckTimeBegin();
458
459 QMutexLocker lock(&GlobalPrivate::mutex);
460
461 PluginMetaQueue result;
462
463 if (srcQueue.isEmpty())
464 return result;
465
466 PluginMetaQueue temp = srcQueue;
467 auto itera = temp.begin();
468 while (itera != temp.end()) {
469 if ((*itera)->depends().isEmpty()) {
470 result.append(*itera);
471 itera = temp.erase(itera);
472 continue;
473 }
474 itera ++;
475 }
476
477 auto dependsNoContains = [=](const PluginMetaQueue &src, const PluginMetaObjectPointer &elem) {
478 QList<QString> result;
479 QList<QString> srcPluginNames;
480 for (auto val : src) {
481 srcPluginNames << val->name();
482 }
483
484 for (auto depend : elem->depends()) {
485 if (!srcPluginNames.contains(depend.name())) {
486 result << depend.name();
487 }
488 }
489 return result;
490 };
491
492 while (!temp.isEmpty()) {
493 auto itera = temp.begin();
494 while (itera != temp.end()) {
495 auto srcNoContains = dependsNoContains(srcQueue, *itera);
496 auto dstNoContains = dependsNoContains(result, *itera);
497 if (!srcNoContains.isEmpty()) {
498 qCritical() << "Error, invalid plugin depends: " << srcNoContains
499 << " from plugin: " << (*itera)->name();
500 for (auto noContainsVal : srcNoContains) {
501 // Delete not existen plugin depend from plugin-name
502 dstNoContains.removeOne(noContainsVal);
503 }
504 // claer not existen plugin depend from plugin-name
505 srcNoContains.clear();
506 }
507
508 if (dstNoContains.isEmpty()) {
509 result.append(*itera);
510 itera = temp.erase(itera);
511 continue;
512 }
513 itera ++;
514 }
515 }
516
517 dpfCheckTimeEnd();
518
519 return result;
520}
521
522/*!
523 * \brief 内部使用QPluginLoader加载所有插件
524 */
525bool PluginManagerPrivate::loadPlugins()
526{
527 dpfCheckTimeBegin();
528
529 loadQueue = dependsSort(readQueue);
530 bool ret = true;
531
532 for (auto val : loadQueue) {
533 if (val->depends().isEmpty()) {
534 QtConcurrent::run(this, &PluginManagerPrivate::loadPlugin, val);
535 } else {
536 ret &= loadPlugin(val);
537 }
538 }
539
540 dpfDebug() << loadQueue;
541 dpfCheckTimeEnd();
542 return ret;
543}
544
545/*!
546 * \brief 初始化所有插件
547 */
548void PluginManagerPrivate::initPlugins()
549{
550 dpfCheckTimeBegin();
551
552 QtConcurrent::map(loadQueue.begin(), loadQueue.end(), [=](auto pointer){
553 this->initPlugin(pointer);
554 }).waitForFinished();
555
556 // 私有类转发进行Sendler闭包
557 emit Listener::instance().d->pluginsInitialized();
558
559 dpfCheckTimeEnd();
560}
561
562/*!
563 * \brief 拉起插件,仅主线程使用
564 */
565void PluginManagerPrivate::startPlugins()
566{
567 dpfCheckTimeBegin();
568
569 for (auto val: loadQueue) {
570 startPlugin(val);
571 }
572
573 // 私有类转发进行Sendler闭包
574 emit Listener::instance().d->pluginsStarted();
575
576 dpfCheckTimeEnd();
577}
578
579/*!
580 * \brief 停止插件,仅主线程
581 */
582void PluginManagerPrivate::stopPlugins()
583{
584 dpfCheckTimeBegin();
585
586 auto itera = loadQueue.rbegin();
587 while(itera != loadQueue.rend()) {
588 stopPlugin(*itera);
589 ++ itera;
590 }
591
592 // 私有类转发进行Sendler闭包
593 emit Listener::instance().d->pluginsStoped();
594
595 dpfCheckTimeEnd();
596}
597
598void PluginManagerPrivate::setSettings(PluginSetting *s)
599{
600 if (setting)
601 delete setting;
602 setting = s;
603}
604
605void PluginManagerPrivate::readSettings()
606{
607 if (setting) {
608 disabledPlugins = setting->value(QLatin1String(DISABLED_PLUGINS)).toStringList();
609 enabledPlugins = setting->value(QLatin1String(ENABLED_PLUGINS)).toStringList();
610 }
611}
612
613void PluginManagerPrivate::writeSettings()
614{
615 if (!setting)
616 return;
617 QStringList tempEnabledPlugins;
618 QStringList tempDisabledPlugins;
619 for (PluginMetaObjectPointer temp: readQueue) {
620 if (temp->isEnabledBySettings()) {
621 tempEnabledPlugins.append(temp->name());
622 } else {
623 tempDisabledPlugins.append(temp->name());
624 }
625 }
626
627 setting->setValue(QLatin1String(ENABLED_PLUGINS), tempEnabledPlugins);
628 setting->setValue(QLatin1String(DISABLED_PLUGINS), tempDisabledPlugins);
629}
630
631DPF_END_NAMESPACE
632