package main import ( "reflect" "github.com/pkg/errors" "github.com/mattermost/mattermost-plugin-bridge-xmpp/server/config" ) // getConfiguration retrieves the active configuration under lock, making it safe to use // concurrently. The active configuration may change underneath the client of this method, but // the struct returned by this API call is considered immutable. func (p *Plugin) getConfiguration() *config.Configuration { p.configurationLock.RLock() defer p.configurationLock.RUnlock() if p.configuration == nil { return &config.Configuration{} } return p.configuration } // setConfiguration replaces the active configuration under lock. // // Do not call setConfiguration while holding the configurationLock, as sync.Mutex is not // reentrant. In particular, avoid using the plugin API entirely, as this may in turn trigger a // hook back into the plugin. If that hook attempts to acquire this lock, a deadlock may occur. // // This method panics if setConfiguration is called with the existing configuration. This almost // certainly means that the configuration was modified without being cloned and may result in // an unsafe access. func (p *Plugin) setConfiguration(configuration *config.Configuration) { p.configurationLock.Lock() defer p.configurationLock.Unlock() if configuration != nil && p.configuration == configuration { // Ignore assignment if the configuration struct is empty. Go will optimize the // allocation for same to point at the same memory address, breaking the check // above. if reflect.ValueOf(*configuration).NumField() == 0 { return } panic("setConfiguration called with the existing configuration") } p.configuration = configuration } // OnConfigurationChange is invoked when configuration changes may have been made. func (p *Plugin) OnConfigurationChange() error { var configuration = new(config.Configuration) // Load the public configuration fields from the Mattermost server configuration. if err := p.API.LoadPluginConfiguration(configuration); err != nil { return errors.Wrap(err, "failed to load plugin configuration") } p.API.LogDebug("Plugin configuration changed") // Validate the configuration if err := configuration.IsValid(); err != nil { p.API.LogError("Configuration validation failed", "error", err, "configuration", configuration) return errors.Wrap(err, "invalid plugin configuration") } p.setConfiguration(configuration) // Update bridge configurations only if bridge manager has been initialized. This prevents a // panic if we are called before OnActivate. if p.bridgeManager != nil { if err := p.bridgeManager.OnPluginConfigurationChange(configuration); err != nil { p.logger.LogWarn("Failed to update bridge configurations", "error", err) } } return nil }