From e9a2aa9745745a17897e76f6524955b2571c03fc Mon Sep 17 00:00:00 2001 From: MiaoWoo Date: Thu, 24 Sep 2020 18:36:10 +0800 Subject: [PATCH] refactor: plugin system & config manager Signed-off-by: MiaoWoo --- packages/plugin/src/command.ts | 12 +-- packages/plugin/src/config.ts | 98 +++++++++++++----------- packages/plugin/src/constants.ts | 4 +- packages/plugin/src/decorators.ts | 4 +- packages/plugin/src/event.ts | 16 ++-- packages/plugin/src/interfaces.ts | 8 ++ packages/plugin/src/loader/ioc-loader.ts | 6 +- packages/plugin/src/manager.ts | 19 +++-- packages/plugin/src/task.ts | 4 +- packages/plugin/src/utils.ts | 2 +- 10 files changed, 99 insertions(+), 74 deletions(-) diff --git a/packages/plugin/src/command.ts b/packages/plugin/src/command.ts index 227f00f8..e880a57d 100644 --- a/packages/plugin/src/command.ts +++ b/packages/plugin/src/command.ts @@ -1,17 +1,17 @@ import { command, plugin, server } from '@ccms/api' -import { provideSingleton, inject } from '@ccms/container' +import { provideSingleton, inject, Autowired } from '@ccms/container' import { getPluginCommandMetadata, getPluginTabCompleterMetadata } from './utils' @provideSingleton(PluginCommandManager) export class PluginCommandManager { - @inject(server.ServerChecker) - private ServerChecker: server.ServerChecker - @inject(command.Command) + @Autowired() private CommandManager: command.Command + @Autowired() + private ServerChecker: server.ServerChecker constructor() { - process.on('plugin.before.enable', (plugin: plugin.Plugin) => this.registryCommand(plugin)) - process.on('plugin.after.disable', (plugin: plugin.Plugin) => this.unregistryCommand(plugin)) + process.on('plugin.before.enable', this.registryCommand.bind(this)) + process.on('plugin.after.disable', this.unregistryCommand.bind(this)) } private registryCommand(pluginInstance: plugin.Plugin) { diff --git a/packages/plugin/src/config.ts b/packages/plugin/src/config.ts index 230b7836..7e0259dc 100644 --- a/packages/plugin/src/config.ts +++ b/packages/plugin/src/config.ts @@ -1,6 +1,9 @@ import * as yaml from 'js-yaml' import * as fs from '@ccms/common/dist/fs' import { plugin } from '@ccms/api' +import { provideSingleton } from '@ccms/container' + +import { interfaces } from './interfaces' import { getPluginConfigMetadata } from './utils' export interface PluginConfigLoader { @@ -13,7 +16,7 @@ export class YamlPluginConfig implements PluginConfigLoader { return yaml.safeLoad(content) } dump(variable: any): string { - return yaml.safeDump(variable, { skipInvalid: true }) + return yaml.safeDump(variable, { skipInvalid: true, lineWidth: 120 }) } } @@ -22,63 +25,68 @@ export class JsonPluginConfig implements PluginConfigLoader { return JSON.parse(content) } dump(variable: any): string { - return JSON.stringify(variable) + return JSON.stringify(variable, undefined, 4) } } -const configLoaderMap = new Map() +@provideSingleton(PluginConfigManager) +export class PluginConfigManager { + private configLoaderMap = new Map() -export function getConfigLoader(format: string) { - if (!configLoaderMap.has(format)) { throw new Error(`Unsupport config format ${format} !`) } - return configLoaderMap.get(format) -} + constructor() { + this.configLoaderMap.set("json", new JsonPluginConfig()) + let yaml = new YamlPluginConfig() + this.configLoaderMap.set("yml", yaml) + this.configLoaderMap.set("yaml", yaml) + process.on('plugin.before.load', this.loadConfig.bind(this)) + process.on('plugin.after.disable', this.saveConfig.bind(this)) + } -function loadConfig(plugin: plugin.Plugin) { - let configs = getPluginConfigMetadata(plugin) - for (let [_, config] of configs) { - try { - let configFile = fs.concat(fs.file(plugin.description.loadMetadata.file).parent, plugin.description.name, config.name + '.' + config.format) - let configFactory = getConfigLoader(config.format) - if (!fs.exists(configFile)) { - base.save(configFile, configFactory.dump(plugin[config.variable])) - console.i18n("ms.plugin.manager.config.save.default", { plugin: plugin.description.name, name: config.name, format: config.format }) - } else { - Object.defineProperty(plugin, config.variable, { value: configFactory.load(base.read(configFile)) }) - plugin[config.variable].save = () => { - let result = configFactory.dump(plugin[config.variable]) - base.save(configFile, result) - console.debug(`[${plugin.description.name}] Save Config ${config.variable} to file ${configFile} result ${result}`) + getConfigLoader(format: string) { + if (!this.configLoaderMap.has(format)) { throw new Error(`Unsupport config format ${format} !`) } + return this.configLoaderMap.get(format) + } + + loadConfig(plugin: plugin.Plugin) { + let configs = getPluginConfigMetadata(plugin) + for (let [_, config] of configs) { + try { + config.file = fs.concat(fs.file(plugin.description.loadMetadata.file).parent, plugin.description.name, config.filename) + let configLoader = this.getConfigLoader(config.format) + if (!fs.exists(config.file)) { + base.save(config.file, configLoader.dump(plugin[config.variable])) + console.i18n("ms.plugin.manager.config.save.default", { plugin: plugin.description.name, name: config.name, format: config.format }) + } else { + Object.defineProperty(plugin, config.variable, { value: configLoader.load(base.read(config.file)) }) + plugin[config.variable].save = () => this.saveConfig0(plugin, config) + console.debug(`[${plugin.description.name}] Load Config ${config.variable} from file ${config.file} =>\n${JSON.stringify(plugin[config.variable], undefined, 4)}`) } - console.debug(`[${plugin.description.name}] Load Config ${config.variable} from file ${configFile} result ${JSON.stringify(plugin[config.variable])}`) + } catch (error) { + console.i18n("ms.plugin.manager.config.load.error", { plugin: plugin.description.name, name: config.name, format: config.format, error }) + console.ex(error) } - } catch (error) { - console.i18n("ms.plugin.manager.config.load.error", { plugin: plugin.description.name, name: config.name, format: config.format, error }) - console.ex(error) } } -} -function saveConfig(plugin: plugin.Plugin) { - let configs = getPluginConfigMetadata(plugin) - for (let [_, config] of configs) { + saveConfig(plugin: plugin.Plugin) { + let configs = getPluginConfigMetadata(plugin) + for (let [_, config] of configs) { + this.saveConfig0(plugin, config) + } + } + + private saveConfig0(plugin: plugin.Plugin, metadata: interfaces.ConfigMetadata) { try { - let configFile = fs.concat(fs.file(plugin.description.loadMetadata.file).parent, plugin.description.name, config.name + '.' + config.format) - let configFactory = getConfigLoader(config.format) - if (!config.readonly) { base.save(configFile, configFactory.dump(plugin[config.variable])) } + if (metadata.readonly) { console.debug(`[${plugin.description.name}] Skip Save Config ${metadata.variable} Because it's readonly!`) } + metadata.file = fs.concat(fs.file(plugin.description.loadMetadata.file).parent, plugin.description.name, metadata.filename) + let result = this.getConfigLoader(metadata.format).dump(plugin[metadata.variable]) + base.save(metadata.file, result) + console.debug(`[${plugin.description.name}] Save Config ${metadata.variable} to file ${metadata.file} =>\n${result}`) + return true } catch (error) { - console.i18n("ms.plugin.manager.config.save.error", { plugin: plugin.description.name, name: config.name, format: config.format, error }) + console.i18n("ms.plugin.manager.config.save.error", { plugin: plugin.description.name, name: metadata.name, format: metadata.format, error }) console.ex(error) + return false } } } - -function init() { - configLoaderMap.set("json", new JsonPluginConfig()) - let yaml = new YamlPluginConfig() - configLoaderMap.set("yml", yaml) - configLoaderMap.set("yaml", yaml) - process.on('plugin.before.load', loadConfig) - process.on('plugin.after.load', saveConfig) -} - -init() diff --git a/packages/plugin/src/constants.ts b/packages/plugin/src/constants.ts index d90ccb57..9ec39df9 100644 --- a/packages/plugin/src/constants.ts +++ b/packages/plugin/src/constants.ts @@ -1,6 +1,6 @@ export const METADATA_KEY = { plugin: Symbol.for("@ccms/plugin:plugin"), - souece: Symbol.for("@ccms/plugin:souece"), + source: Symbol.for("@ccms/plugin:source"), cmd: Symbol.for("@ccms/plugin:cmd"), tab: Symbol.for("@ccms/plugin:tab"), listener: Symbol.for("@ccms/plugin:listener"), @@ -10,4 +10,4 @@ export const METADATA_KEY = { enable: Symbol.for("@ccms/plugin:stage:enable"), disable: Symbol.for("@ccms/plugin:stage:disable") } -}; +} diff --git a/packages/plugin/src/decorators.ts b/packages/plugin/src/decorators.ts index 6c85bf9e..6ced8904 100644 --- a/packages/plugin/src/decorators.ts +++ b/packages/plugin/src/decorators.ts @@ -19,7 +19,7 @@ export function plugin(metadata: pluginApi.PluginMetadata | any) { Reflect.defineMetadata(METADATA_KEY.plugin, previousMetadata, Reflect) const previousSources: Map = getPluginSources() previousSources.set(metadata.source.toString(), metadata) - Reflect.defineMetadata(METADATA_KEY.souece, previousSources, Reflect) + Reflect.defineMetadata(METADATA_KEY.source, previousSources, Reflect) } } @@ -77,9 +77,11 @@ export function config(metadata: interfaces.ConfigMetadata = {}) { metadata.variable = key metadata.version = metadata.version ?? 1 metadata.format = metadata.format ?? 'yml' + metadata.filename = metadata.filename ?? metadata.name + '.' + metadata.format let previousMetadata = getPluginConfigMetadata(target) previousMetadata.set(metadata.name, metadata) Reflect.defineMetadata(METADATA_KEY.config, previousMetadata, target.constructor) + Reflect.defineMetadata(METADATA_KEY.config, metadata, target[key]) } } diff --git a/packages/plugin/src/event.ts b/packages/plugin/src/event.ts index 4ed5cdf3..471019bb 100644 --- a/packages/plugin/src/event.ts +++ b/packages/plugin/src/event.ts @@ -1,17 +1,21 @@ import { event, plugin, server } from '@ccms/api' -import { provideSingleton, postConstruct, inject } from '@ccms/container' +import { provideSingleton, Autowired } from '@ccms/container' import { getPluginListenerMetadata } from './utils' @provideSingleton(PluginEventManager) export class PluginEventManager { - @inject(server.ServerChecker) - private ServerChecker: server.ServerChecker - @inject(event.Event) + @Autowired() private EventManager: event.Event + @Autowired() + private ServerChecker: server.ServerChecker constructor() { - process.on('plugin.before.enable', (plugin: plugin.Plugin) => this.registryListener(plugin)) - process.on('plugin.after.disable', (plugin: plugin.Plugin) => this.unregistryListener(plugin)) + process.on('plugin.before.enable', this.registryListener.bind(this)) + process.on('plugin.after.disable', this.unregistryListener.bind(this)) + } + + mapEventName() { + return this.EventManager.mapEventName().toFixed(0) } private registryListener(pluginInstance: plugin.Plugin) { diff --git a/packages/plugin/src/interfaces.ts b/packages/plugin/src/interfaces.ts index 0a8d9a19..bdc87ea1 100644 --- a/packages/plugin/src/interfaces.ts +++ b/packages/plugin/src/interfaces.ts @@ -75,5 +75,13 @@ export namespace interfaces { * 是否为只读(关闭时将不会自动保存) */ readonly?: boolean + /** + * 配置文件名称 + */ + filename?: string + /** + * 配置文件全路径 + */ + file?: any } } diff --git a/packages/plugin/src/loader/ioc-loader.ts b/packages/plugin/src/loader/ioc-loader.ts index 2d272245..311c2bde 100644 --- a/packages/plugin/src/loader/ioc-loader.ts +++ b/packages/plugin/src/loader/ioc-loader.ts @@ -1,5 +1,5 @@ import { plugin, server } from "@ccms/api" -import { inject, ContainerInstance, Container, provideSingletonNamed } from "@ccms/container" +import { inject, ContainerInstance, Container, provideSingletonNamed, Autowired } from "@ccms/container" import { interfaces } from "../interfaces" import { getPluginStageMetadata, getPluginSources } from "../utils" @@ -11,7 +11,7 @@ export class IocLoader implements plugin.PluginLoader { type: string = LOADER_TYPE_NAME @inject(ContainerInstance) private container: Container - @inject(server.ServerChecker) + @Autowired() private serverChecker: server.ServerChecker private pluginMetadataMap: Map @@ -69,7 +69,7 @@ export class IocLoader implements plugin.PluginLoader { console.i18n('ms.plugin.manager.build.duplicate', { exists: pluginInstance.description.source, source: metadata.source }) } this.container.rebind(plugin.Plugin).to(metadata.target).inSingletonScope().whenTargetNamed(metadata.name) - } catch{ + } catch { this.container.bind(plugin.Plugin).to(metadata.target).inSingletonScope().whenTargetNamed(metadata.name) } } diff --git a/packages/plugin/src/manager.ts b/packages/plugin/src/manager.ts index 1829ef0a..e4faa9c7 100644 --- a/packages/plugin/src/manager.ts +++ b/packages/plugin/src/manager.ts @@ -1,12 +1,13 @@ import i18n from '@ccms/i18n' import { plugin, server, event } from '@ccms/api' -import { inject, provideSingleton, Container, ContainerInstance } from '@ccms/container' +import { inject, provideSingleton, Container, ContainerInstance, Autowired } from '@ccms/container' import './config' import { interfaces } from './interfaces' import { PluginTaskManager } from './task' import { PluginEventManager } from './event' import { PluginCommandManager } from './command' +import { PluginConfigManager } from './config' const Thread = Java.type('java.lang.Thread') @@ -18,16 +19,17 @@ export class PluginManagerImpl implements plugin.PluginManager { private pluginInstance: any @inject(server.ServerType) private serverType: string - @inject(event.Event) - private EventManager: event.Event - @inject(server.ServerChecker) + + @Autowired() private serverChecker: server.ServerChecker - @inject(PluginTaskManager) + @Autowired() private taskManager: PluginTaskManager - @inject(PluginEventManager) + @Autowired() private eventManager: PluginEventManager - @inject(PluginCommandManager) + @Autowired() + private configManager: PluginConfigManager + @Autowired() private commandManager: PluginCommandManager private initialized: boolean = false @@ -48,6 +50,7 @@ export class PluginManagerImpl implements plugin.PluginManager { // ignore unused this.taskManager this.eventManager + this.configManager this.commandManager } @@ -55,7 +58,7 @@ export class PluginManagerImpl implements plugin.PluginManager { if (this.pluginInstance === undefined) { throw new Error("Can't found Plugin Instance!") } if (this.initialized !== true) { console.i18n('ms.plugin.initialize', { plugin: this.pluginInstance, loader: Thread.currentThread().contextClassLoader }) - console.i18n('ms.plugin.event.map', { count: this.EventManager.mapEventName().toFixed(0), type: this.serverType }) + console.i18n('ms.plugin.event.map', { count: this.eventManager.mapEventName(), type: this.serverType }) let pluginScanner = this.container.getAll(plugin.PluginScanner) pluginScanner.forEach((scanner) => { console.debug(`loading plugin sacnner ${scanner.type}...`) diff --git a/packages/plugin/src/task.ts b/packages/plugin/src/task.ts index 853ce70d..7af21278 100644 --- a/packages/plugin/src/task.ts +++ b/packages/plugin/src/task.ts @@ -1,9 +1,9 @@ import { plugin, task } from '@ccms/api' -import { provideSingleton, inject } from '@ccms/container' +import { provideSingleton, Autowired } from '@ccms/container' @provideSingleton(PluginTaskManager) export class PluginTaskManager { - @inject(task.TaskManager) + @Autowired() private taskManager: task.TaskManager constructor() { diff --git a/packages/plugin/src/utils.ts b/packages/plugin/src/utils.ts index 0791cc35..154ac238 100644 --- a/packages/plugin/src/utils.ts +++ b/packages/plugin/src/utils.ts @@ -14,7 +14,7 @@ function getPlugin(name: string) { function getPluginSources() { let pluginSources: Map = Reflect.getMetadata( - METADATA_KEY.souece, + METADATA_KEY.source, Reflect ) || pluginSourceCache return pluginSources