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