-
Notifications
You must be signed in to change notification settings - Fork 20
/
context.go
376 lines (313 loc) · 9.47 KB
/
context.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
// SPDX-FileCopyrightText: 2022 SAP SE or an SAP affiliate company and Open Component Model contributors.
//
// SPDX-License-Identifier: Apache-2.0
package datacontext
import (
"context"
"fmt"
"io"
"reflect"
"sync"
"github.com/mandelsoft/logging"
"github.com/open-component-model/ocm/pkg/common"
"github.com/open-component-model/ocm/pkg/contexts/datacontext/action/handlers"
"github.com/open-component-model/ocm/pkg/errors"
"github.com/open-component-model/ocm/pkg/finalizer"
ocmlog "github.com/open-component-model/ocm/pkg/logging"
"github.com/open-component-model/ocm/pkg/runtime"
"github.com/open-component-model/ocm/pkg/utils"
)
const OCM_CONTEXT_SUFFIX = ".context" + common.OCM_TYPE_GROUP_SUFFIX
// BuilderMode controls the handling of unset information in the
// builder configuration when calling the New method.
type BuilderMode int
const (
// MODE_SHARED uses the default contexts for unset nested context types.
MODE_SHARED BuilderMode = iota
// MODE_DEFAULTED uses dedicated context instances configured with the
// context type specific default registrations.
MODE_DEFAULTED
// MODE_EXTENDED uses dedicated context instances configured with
// context type registrations extending the default registrations.
MODE_EXTENDED
// MODE_CONFIGURED uses dedicated context instances configured with the
// context type registrations configured with the actual state of the
// default registrations.
MODE_CONFIGURED
// MODE_INITIAL uses completely new contexts for unset nested context types
// and initial registrations.
MODE_INITIAL
)
func (m BuilderMode) String() string {
switch m {
case MODE_SHARED:
return "shared"
case MODE_DEFAULTED:
return "defaulted"
case MODE_EXTENDED:
return "extended"
case MODE_CONFIGURED:
return "configured"
case MODE_INITIAL:
return "initial"
default:
return fmt.Sprintf("(invalid %d)", m)
}
}
func Mode(m ...BuilderMode) BuilderMode {
return utils.OptionalDefaulted(MODE_EXTENDED, m...)
}
type ContextIdentity = finalizer.ObjectIdentity
type ContextProvider interface {
// AttributesContext returns the shared attributes
AttributesContext() AttributesContext
}
// Delegates is the interface for common
// Context features, which might be delegated
// to aggregated contexts.
type Delegates interface {
ocmlog.LogProvider
handlers.ActionsProvider
}
type _delegates struct {
logging logging.Context
actions handlers.Registry
}
func (d _delegates) LoggingContext() logging.Context {
return d.logging
}
func (d _delegates) Logger(messageContext ...logging.MessageContext) logging.Logger {
return d.logging.Logger(messageContext)
}
func (d _delegates) GetActions() handlers.Registry {
return d.actions
}
func ComposeDelegates(l logging.Context, a handlers.Registry) Delegates {
return _delegates{l, a}
}
type ContextBinder interface {
// BindTo binds the context to a context.Context and makes it
// retrievable by a ForContext method
BindTo(ctx context.Context) context.Context
}
// Context describes a common interface for a data context used for a dedicated
// purpose.
// Such has a type and always specific attribute store.
// Every Context can be bound to a context.Context.
type Context interface {
ContextBinder
ContextProvider
Delegates
// GetType returns the context type
GetType() string
GetId() ContextIdentity
GetAttributes() Attributes
Finalize() error
Finalizer() *finalizer.Finalizer
}
type InternalContext interface {
Context
finalizer.RecorderProvider
GetKey() interface{}
Cleanup() error
}
////////////////////////////////////////////////////////////////////////////////
// CONTEXT_TYPE is the global type for an attribute context.
const CONTEXT_TYPE = "attributes" + OCM_CONTEXT_SUFFIX
type AttributesContext interface {
Context
BindTo(ctx context.Context) context.Context
}
// AttributeFactory is used to atomicly create a new attribute for a context.
type AttributeFactory func(Context) interface{}
type Attributes interface {
finalizer.Finalizable
GetAttribute(name string, def ...interface{}) interface{}
SetAttribute(name string, value interface{}) error
SetEncodedAttribute(name string, data []byte, unmarshaller runtime.Unmarshaler) error
GetOrCreateAttribute(name string, creator AttributeFactory) interface{}
}
// DefaultContext is the default context initialized by init functions.
var DefaultContext = NewWithActions(nil, handlers.DefaultRegistry())
// ForContext returns the Context to use for context.Context.
// This is either an explicit context or the default context.
func ForContext(ctx context.Context) AttributesContext {
c, _ := ForContextByKey(ctx, key, DefaultContext)
if c == nil {
return nil
}
return c.(AttributesContext)
}
// WithContext create a new Context bound to a context.Context.
func WithContext(ctx context.Context, parentAttrs Attributes) (Context, context.Context) {
c := New(parentAttrs)
return c, c.BindTo(ctx)
}
////////////////////////////////////////////////////////////////////////////////
type Updater interface {
Update() error
}
type UpdateFunc func() error
func (u UpdateFunc) Update() error {
return u()
}
type delegates = Delegates
////////////////////////////////////////////////////////////////////////////////
var key = reflect.TypeOf(_context{})
type _context struct {
*contextBase
updater Updater
}
// gcWrapper is used as garbage collectable
// wrapper for a context implementation
// to establish a runtime finalizer.
type gcWrapper struct {
GCWrapper
*_context
}
func (w *gcWrapper) SetContext(c *_context) {
w._context = c
}
// New provides a root attribute context.
func New(parentAttrs ...Attributes) AttributesContext {
return NewWithActions(utils.Optional(parentAttrs...), handlers.NewRegistry(nil, handlers.DefaultRegistry()))
}
func NewWithActions(parentAttrs Attributes, actions handlers.Registry) AttributesContext {
return newWithActions(MODE_DEFAULTED, parentAttrs, actions)
}
func newWithActions(mode BuilderMode, parentAttrs Attributes, actions handlers.Registry) AttributesContext {
c := &_context{}
c.contextBase = newContextBase(c, CONTEXT_TYPE, key, parentAttrs, &c.updater,
ComposeDelegates(logging.NewWithBase(ocmlog.Context()), handlers.NewRegistry(nil, actions)),
)
return SetupContext(mode, FinalizedContext[gcWrapper](c))
}
func (c *_context) Actions() handlers.Registry {
if c.updater != nil {
c.updater.Update()
}
return c.contextBase.GetActions()
}
func (c *_context) LoggingContext() logging.Context {
if c.updater != nil {
c.updater.Update()
}
return c.contextBase.LoggingContext()
}
func (c *_context) Logger(messageContext ...logging.MessageContext) logging.Logger {
if c.updater != nil {
c.updater.Update()
}
return c.contextBase.Logger(messageContext...)
}
////////////////////////////////////////////////////////////////////////////////
var contextrange, attrsrange = finalizer.NumberRange{}, finalizer.NumberRange{}
type _attributes struct {
sync.RWMutex
id uint64
ctx Context
parent Attributes
updater *Updater
attributes map[string]interface{}
}
var _ Attributes = &_attributes{}
func NewAttributes(ctx Context, parent Attributes, updater *Updater) Attributes {
return newAttributes(ctx, parent, updater)
}
func newAttributes(ctx Context, parent Attributes, updater *Updater) *_attributes {
return &_attributes{
id: attrsrange.NextId(),
ctx: ctx,
parent: parent,
updater: updater,
attributes: map[string]interface{}{},
}
}
func (c *_attributes) Finalize() error {
list := errors.ErrListf("finalizing attributes")
for n, a := range c.attributes {
if f, ok := a.(finalizer.Finalizable); ok {
list.Addf(nil, f.Finalize(), "attribute %s", n)
}
}
return list.Result()
}
func (c *_attributes) GetAttribute(name string, def ...interface{}) interface{} {
if *c.updater != nil {
(*c.updater).Update()
}
c.RLock()
defer c.RUnlock()
if a := c.attributes[name]; a != nil {
return a
}
if c.parent != nil {
if a := c.parent.GetAttribute(name); a != nil {
return a
}
}
return utils.Optional(def...)
}
func (c *_attributes) SetEncodedAttribute(name string, data []byte, unmarshaller runtime.Unmarshaler) error {
s := DefaultAttributeScheme.Shortcuts()[name]
if s != "" {
name = s
}
v, err := DefaultAttributeScheme.Decode(name, data, unmarshaller)
if err != nil {
return err
}
c.SetAttribute(name, v)
return nil
}
func (c *_attributes) setAttribute(name string, value interface{}) error {
c.Lock()
defer c.Unlock()
_, err := DefaultAttributeScheme.Encode(name, value, nil)
if err != nil && !errors.IsErrUnknownKind(err, "attribute") {
return err
}
old := c.attributes[name]
if old != nil && old != value {
if c, ok := old.(io.Closer); ok {
c.Close()
}
}
value, err = DefaultAttributeScheme.Convert(name, value)
if err != nil && !errors.IsErrUnknownKind(err, "attribute") {
return err
}
c.attributes[name] = value
return nil
}
func (c *_attributes) SetAttribute(name string, value interface{}) error {
err := c.setAttribute(name, value)
if err == nil {
if *c.updater != nil {
(*c.updater).Update()
}
}
return err
}
func (c *_attributes) getOrCreateAttribute(name string, creator AttributeFactory) interface{} {
c.Lock()
defer c.Unlock()
if v := c.attributes[name]; v != nil {
return v
}
if c.parent != nil {
if v := c.parent.GetAttribute(name); v != nil {
return v
}
}
v := creator(c.ctx)
c.attributes[name] = v
return v
}
func (c *_attributes) GetOrCreateAttribute(name string, creator AttributeFactory) interface{} {
r := c.getOrCreateAttribute(name, creator)
if *c.updater != nil {
(*c.updater).Update()
}
return r
}