* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
* Global Trust Authority is licensed under the Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
* PURPOSE.
* See the Mulan PSL v2 for more details.
*/
use libloading::{Library, Symbol};
use std::collections::HashMap;
use std::error::Error;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, OnceLock, RwLock};
use log::{error, info};
use crate::host_functions::{AgentHostFunctions, HostFunctions, ServiceHostFunctions};
use crate::traits::{AgentPlugin, PluginBase, PluginManagerInstance, ServicePlugin};
pub struct PluginEntry {
pub(crate) _lib: Library,
}
pub type CreatePluginFn<T, H> = fn(&H, &str) -> Result<Box<T>, Box<dyn Error>>;
pub struct PluginManager<T: PluginBase + ?Sized, H: HostFunctions> {
plugins: RwLock<HashMap<String, (Arc<T>, PluginEntry)>>,
initialized: AtomicBool,
_phantom: std::marker::PhantomData<H>,
}
impl<T: PluginBase + ?Sized + 'static, H: HostFunctions> PluginManager<T, H> {
fn new() -> Self {
Self {
plugins: RwLock::new(HashMap::new()),
initialized: AtomicBool::new(false),
_phantom: std::marker::PhantomData,
}
}
fn register_plugin(&self, name: String, plugin: Box<T>, lib: Library) -> Result<(), String> {
let mut plugins =
self.plugins.write().map_err(|e| format!("Failed to acquire write lock for plugin registration: {e}"))?;
let entry = PluginEntry { _lib: lib };
plugins.insert(name, (Arc::from(plugin), entry));
Ok(())
}
pub fn get_plugin(&self, name: &str) -> Option<Arc<T>> {
let plugins = self.plugins.read().ok()?;
plugins.get(name).map(|(plugin, _)| plugin.clone())
}
pub fn get_plugin_types(&self) -> Vec<String> {
match self.plugins.read() {
Ok(plugins) => plugins.keys().cloned().collect(),
Err(e) => {
error!("Failed to acquire read lock for plugin types: {e}");
Vec::new()
},
}
}
pub fn is_initialized(&self) -> bool {
self.initialized.load(Ordering::Relaxed)
}
unsafe fn load_plugin(&self, name: &str, path: &str, host_functions: &H) -> Result<(), String> {
let lib = Library::new(path).map_err(|e| format!("Failed to load library {path}: {e}"))?;
let constructor = lib
.get::<Symbol<'_, CreatePluginFn<T, H>>>(b"create_plugin")
.map_err(|e| format!("Failed to find create_plugin symbol: {e}"))?;
let plugin = constructor(host_functions, name).map_err(|e| format!("Plugin {name} creation failed for {e}"))?;
if plugin.plugin_type() != name {
return Err(format!("Plugin type mismatch for {name}"));
}
self.register_plugin(name.to_string(), plugin, lib)?;
Ok(())
}
pub fn initialize(&self, plugin_paths: &HashMap<String, String>, host_functions: &H) -> bool {
info!("Initializing plugin manager with {} plugins", plugin_paths.len());
let mut all_successful = true;
for (name, path) in plugin_paths {
info!("Loading plugin '{name}' from path: {path}");
let result = unsafe { self.load_plugin(name, path, host_functions) };
if let Err(error) = result {
error!("Error loading plugin {name}: {error}");
all_successful = false;
} else {
info!("Successfully registered plugin: {name}");
}
}
self.initialized.store(all_successful, Ordering::Relaxed);
if all_successful {
info!("Plugin manager successfully initialized with all plugins");
} else {
error!("Plugin manager initialization completed with errors");
}
all_successful
}
}
impl PluginManagerInstance for PluginManager<dyn ServicePlugin, ServiceHostFunctions> {
fn get_instance() -> &'static Self {
static INSTANCE: OnceLock<PluginManager<dyn ServicePlugin, ServiceHostFunctions>> = OnceLock::new();
INSTANCE.get_or_init(|| PluginManager::new())
}
}
impl PluginManagerInstance for PluginManager<dyn AgentPlugin, AgentHostFunctions> {
fn get_instance() -> &'static Self {
static INSTANCE: OnceLock<PluginManager<dyn AgentPlugin, AgentHostFunctions>> = OnceLock::new();
INSTANCE.get_or_init(|| PluginManager::new())
}
}