pkg/plugin/loader.go (102 lines of code) (raw):

/* * 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) }