loader logic revised
This commit is contained in:
+75
-60
@@ -1,3 +1,11 @@
|
||||
/*
|
||||
Пакет loader - основная точка входа в клиенское приложение.
|
||||
|
||||
Задачи:
|
||||
- Загрузка и выполнение плагина, в котором сосредоточена логика основного UI,
|
||||
- Выполнение операций с локальным Storage,
|
||||
- Выполнение операций обмена данными с сервером.
|
||||
*/
|
||||
package loader
|
||||
|
||||
import (
|
||||
@@ -64,50 +72,78 @@ func NewLoader(s storage.Storage, conn connector.Connector, app fyne.App) (*load
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// updatePluginFromVersion заменяет файл {libUIPluginFile} с плагином на новую версию, которая была ранее загружена и сохранена в State
|
||||
func (l *loader) updatePluginFromVersion() error {
|
||||
state, stateExists, err := l.loadCurrentState()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Load State error: %w", err)
|
||||
}
|
||||
if !stateExists {
|
||||
return nil
|
||||
}
|
||||
if state.ClientNextVersion == nil {
|
||||
return nil
|
||||
}
|
||||
version := *state.ClientNextVersion
|
||||
versionFileName := resolvePluginFile(version)
|
||||
versionFileExists, _, err := l.storage.FileExists(versionFileName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Check plugin v%s exists error: %w", version, err)
|
||||
}
|
||||
if !versionFileExists {
|
||||
return fmt.Errorf("Requested plugin v%s does not exists at local storage", version)
|
||||
}
|
||||
storedData, err := l.storage.ReadFile(versionFileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := l.storage.WriteFile(libUIPluginFile, storedData); err != nil {
|
||||
return fmt.Errorf("Plugin file write error: %w", err)
|
||||
}
|
||||
state.ClientCurrentVersion = version
|
||||
state.ClientNextVersion = nil
|
||||
if err := l.storage.SaveState(*state); err != nil {
|
||||
return fmt.Errorf("State write error: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *loader) initPlugin() (mc.Client, error) {
|
||||
exists, pluginPath, err := l.storage.FileExists(libUIPluginFile)
|
||||
pluginExists, pluginPath, err := l.storage.FileExists(libUIPluginFile)
|
||||
if err != nil {
|
||||
l.appendFatalError(fmt.Errorf("Client plugin file lookup error: %w", err))
|
||||
return nil, err
|
||||
}
|
||||
if !exists {
|
||||
|
||||
if !pluginExists {
|
||||
l.logText("Client plugin file not found, fetching available versions")
|
||||
v, err := l.connector.CheckVersion()
|
||||
err = l.checkAndDownloadPluginVersion()
|
||||
if err != nil {
|
||||
l.logError(err)
|
||||
return nil, err
|
||||
}
|
||||
l.logText(fmt.Sprintf("Received %d versions", len(v)))
|
||||
latest, ok, err := latestVersion(v)
|
||||
if err != nil {
|
||||
l.logError(err)
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
l.logError(errors.New("Server did not responded with a suitable client version"))
|
||||
return nil, err
|
||||
}
|
||||
l.logText(fmt.Sprintf("Downloading version %s", latest.Version))
|
||||
data, err := l.connector.DownloadVersion(latest.URL)
|
||||
if err != nil {
|
||||
l.logError(fmt.Errorf("Version %s download error: %w", latest.Version, err))
|
||||
return nil, err
|
||||
}
|
||||
err = l.storage.WriteFile(libUIPluginFile, data)
|
||||
if err != nil {
|
||||
l.appendFatalError(fmt.Errorf("Plugin file write error: %w", err))
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
err = l.updatePluginFromVersion()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
l.logText(fmt.Sprintf("Loading client plugin from %s", pluginPath))
|
||||
cli, err := loadClientPlugin(l.storage, l.connector, l.app, pluginPath, pluginInitSymbol)
|
||||
if err != nil {
|
||||
l.appendFatalError(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// need to store current plugin version for the very first app start
|
||||
if err := l.updateCurrentVersion(cli.Version()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cli, nil
|
||||
}
|
||||
|
||||
// init инициализирует плагин клиента и запускает его, либо отображает пользователю лог с ошибками
|
||||
func (l *loader) init() {
|
||||
l.fatalError = false
|
||||
fyne.Do(func() {
|
||||
@@ -161,9 +197,15 @@ func (l *loader) appendFatalError(err error) {
|
||||
l.fatalError = true
|
||||
}
|
||||
|
||||
// Run is the main entry point for start Client
|
||||
func (l *loader) Run(ctx context.Context) error {
|
||||
// start initializing process
|
||||
go l.init()
|
||||
|
||||
// run UI engine, not the main Client app
|
||||
l.app.Run()
|
||||
|
||||
// wait for successfull 'loaded' signal from init()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
@@ -172,50 +214,23 @@ func (l *loader) Run(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
// runClient запусукает основного Client и блокирует выполнение в текущем потоке
|
||||
func (l *loader) runClient(ctx context.Context) error {
|
||||
if l.client == nil {
|
||||
return errors.New("run: client wasn't initialized, this is an program fatal error.")
|
||||
}
|
||||
final := make(chan struct{}, 1)
|
||||
go l.backgroundLoop(ctx, final)
|
||||
defer func() { final <- struct{}{} }()
|
||||
|
||||
bgSignal := make(chan struct{}, 1)
|
||||
go l.backgroundLoop(ctx, bgSignal)
|
||||
// stop backbround loop before exin func
|
||||
defer func() { bgSignal <- struct{}{} }()
|
||||
|
||||
// Client's Run() blocks execution until window is closed, or error is returned immediately
|
||||
if err := l.client.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
final <- struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *loader) backgroundLoop(ctx context.Context, final <-chan struct{}) {
|
||||
checkConnTimer := time.NewTimer(checkConnectionTimeout)
|
||||
checkVersionTimer := time.NewTimer(checkVersionTimeout)
|
||||
defer func() {
|
||||
checkConnTimer.Stop()
|
||||
checkVersionTimer.Stop()
|
||||
}()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
l.client.Shutdown()
|
||||
return
|
||||
case <-final:
|
||||
return
|
||||
case <-checkConnTimer.C:
|
||||
isGood := l.connector.CheckConnection()
|
||||
l.client.OnConnection(isGood)
|
||||
checkConnTimer.Reset(checkConnectionTimeout)
|
||||
case <-checkVersionTimer.C:
|
||||
versions, err := l.connector.CheckVersion()
|
||||
if err != nil {
|
||||
// propagate error to the UI
|
||||
} else if latest, ok, err := latestVersion(versions); err != nil {
|
||||
// propagate error to the UI
|
||||
} else if ok {
|
||||
l.downloadVersion(latest)
|
||||
}
|
||||
checkVersionTimer.Reset(checkVersionTimeout)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadClientPlugin loads a Client implementation from a shared plugin file at the specified path.
|
||||
|
||||
Reference in New Issue
Block a user