blob: 22f4acf71ddddbff6a056434d0c3b7b384e9943e [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.
*/
package plugin
import (
"fmt"
"os"
"path/filepath"
"plugin"
"regexp"
"sync"
"github.com/apache/servicecomb-service-center/pkg/log"
)
var (
loader Loader
once sync.Once
regex, _ = regexp.Compile(`([A-Za-z0-9_.-]+)_plugin.so$`)
)
type wrapPlugin struct {
p *plugin.Plugin
funcs map[string]plugin.Symbol
}
type Loader struct {
Plugins map[string]*wrapPlugin
mux sync.RWMutex
}
func (pm *Loader) Init() {
pm.Plugins = make(map[string]*wrapPlugin, 10)
err := pm.ReloadPlugins()
if len(pm.Plugins) == 0 {
log.Error("no any plugin has been loaded", err)
}
}
func (pm *Loader) ReloadPlugins() error {
dir := os.ExpandEnv(GetConfigurator().GetPluginDir())
if len(dir) == 0 {
dir, _ = os.Getwd()
}
if len(dir) == 0 {
return fmt.Errorf("'plugins_dir' is unset")
}
files, err := os.ReadDir(dir)
if err != nil {
return err
}
for _, file := range files {
if !file.Type().IsRegular() {
continue
}
submatchs := regex.FindStringSubmatch(file.Name())
if len(submatchs) < 2 {
continue
}
// golang 1.8+ feature
pluginFileFullPath := filepath.Join(dir, file.Name())
p, err := plugin.Open(pluginFileFullPath)
if err != nil {
return fmt.Errorf("load plugin '%s' error for %s", submatchs[1], err.Error())
}
log.Info(fmt.Sprintf("load plugin '%s' successfully", submatchs[1]))
pm.mux.Lock()
pm.Plugins[submatchs[1]] = &wrapPlugin{p, make(map[string]plugin.Symbol, 10)}
pm.mux.Unlock()
}
return nil
}
func (pm *Loader) Find(pluginName, funcName string) (plugin.Symbol, error) {
pm.mux.RLock()
w, ok := pm.Plugins[pluginName]
if !ok {
pm.mux.RUnlock()
return nil, fmt.Errorf("can not find plugin '%s'", pluginName)
}
f, ok := w.funcs[funcName]
if ok {
pm.mux.RUnlock()
return f, nil
}
pm.mux.RUnlock()
pm.mux.Lock()
var err error
f, err = w.p.Lookup(funcName)
if err != nil {
pm.mux.Unlock()
return nil, err
}
w.funcs[funcName] = f
pm.mux.Unlock()
return f, nil
}
func (pm *Loader) Exist(pluginName string) bool {
pm.mux.RLock()
_, ok := pm.Plugins[pluginName]
pm.mux.RUnlock()
return ok
}
func GetLoader() *Loader {
once.Do(loader.Init)
return &loader
}
func Reload() error {
return GetLoader().ReloadPlugins()
}
func FindFunc(pluginName, funcName string) (plugin.Symbol, error) {
return GetLoader().Find(pluginName, funcName)
}