blob: 01091ed235ec66685b6f3f2006fe4d229614aaee [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package factory
import (
"errors"
"fmt"
"os"
"sync"
"v.io/v23/context"
"v.io/v23/discovery"
idiscovery "v.io/x/ref/lib/discovery"
)
type (
pluginFactory func(ctx *context.T, host string) (idiscovery.Plugin, error)
pluginFactoryMap map[string]pluginFactory
)
var (
pluginFactories pluginFactoryMap
)
// SetPluginFactory sets the plugin factory with the given name.
// This should be called before v23.NewDiscovery() is called.
func SetPluginFactory(name string, factory pluginFactory) {
pluginFactories[name] = factory
}
type lazyFactory struct {
host string
protocols []string
once sync.Once
f idiscovery.Factory
err error
}
func (l *lazyFactory) New(ctx *context.T) (discovery.T, error) {
l.once.Do(func() { l.f, l.err = newFactory(ctx, l.host, l.protocols) })
if l.err != nil {
return nil, l.err
}
return l.f.New(ctx)
}
func (l *lazyFactory) Shutdown() {
l.once.Do(func() { l.err = errors.New("factory closed") })
if l.f != nil {
l.f.Shutdown()
}
}
// New returns a new discovery factory with the given protocols.
//
// We instantiate a factory lazily so that we do not turn it on until
// it is actually used.
func New(_ *context.T, protocols ...string) (idiscovery.Factory, error) {
host, _ := os.Hostname()
if len(host) == 0 {
// TODO(jhahn): Should we handle error here?
host = "v23"
}
if len(protocols) == 0 {
// Use all registered protocols.
for p, _ := range pluginFactories {
protocols = append(protocols, p)
}
}
// Verify protocols.
for _, p := range protocols {
if _, exists := pluginFactories[p]; !exists {
return nil, fmt.Errorf("discovery protocol %q is not supported", p)
}
}
return &lazyFactory{host: host, protocols: protocols}, nil
}
func newFactory(ctx *context.T, host string, protocols []string) (idiscovery.Factory, error) {
if injectedFactory != nil {
return injectedFactory, nil
}
plugins := make([]idiscovery.Plugin, 0, len(protocols))
for _, p := range protocols {
factory := pluginFactories[p]
plugin, err := factory(ctx, host)
if err != nil {
return nil, err
}
plugins = append(plugins, plugin)
}
return idiscovery.NewFactory(ctx, plugins...)
}
var injectedFactory idiscovery.Factory
// InjectFactory allows a runtime to use the given discovery factory. This
// should be called before v23.NewDiscovery() is called. Mostly used for testing.
func InjectFactory(factory idiscovery.Factory) {
injectedFactory = factory
}