X-Git-Url: https://gerrit.akraino.org/r/gitweb?a=blobdiff_plain;f=src%2Ffoundation%2Fapi%2Frevel%2Fintercept.go;fp=src%2Ffoundation%2Fapi%2Frevel%2Fintercept.go;h=2ee867735259b215388d67e3acdb178f4372048d;hb=1d1ee6961c93781e1187d8c7faa868da6b2f01f4;hp=0000000000000000000000000000000000000000;hpb=56dd5e0f2164b37b40ac1daa188ccc618b4cbd19;p=iec.git diff --git a/src/foundation/api/revel/intercept.go b/src/foundation/api/revel/intercept.go new file mode 100644 index 0000000..2ee8677 --- /dev/null +++ b/src/foundation/api/revel/intercept.go @@ -0,0 +1,249 @@ +// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved. +// Revel Framework source code and usage is governed by a MIT style +// license that can be found in the LICENSE file. + +package revel + +import ( + "log" + "reflect" + "sort" +) + +// An InterceptorFunc is functionality invoked by the framework BEFORE or AFTER +// an action. +// +// An interceptor may optionally return a Result (instead of nil). Depending on +// when the interceptor was invoked, the response is different: +// 1. BEFORE: No further interceptors are invoked, and neither is the action. +// 2. AFTER: Further interceptors are still run. +// In all cases, any returned Result will take the place of any existing Result. +// +// In the BEFORE case, that returned Result is guaranteed to be final, while +// in the AFTER case it is possible that a further interceptor could emit its +// own Result. +// +// Interceptors are called in the order that they are added. +// +// *** +// +// Two types of interceptors are provided: Funcs and Methods +// +// Func Interceptors may apply to any / all Controllers. +// +// func example(*revel.Controller) revel.Result +// +// Method Interceptors are provided so that properties can be set on application +// controllers. +// +// func (c AppController) example() revel.Result +// func (c *AppController) example() revel.Result +// +type InterceptorFunc func(*Controller) Result +type InterceptorMethod interface{} +type When int + +const ( + BEFORE When = iota + AFTER + PANIC + FINALLY +) + +type InterceptTarget int + +const ( + AllControllers InterceptTarget = iota +) + +type Interception struct { + When When + + function InterceptorFunc + method InterceptorMethod + + callable reflect.Value + target reflect.Type + interceptAll bool +} + +// Invoke performs the given interception. +// val is a pointer to the App Controller. +func (i Interception) Invoke(val reflect.Value, target *reflect.Value) reflect.Value { + var arg reflect.Value + if i.function == nil { + // If it's an InterceptorMethod, then we have to pass in the target type. + arg = *target + } else { + // If it's an InterceptorFunc, then the type must be *Controller. + // We can find that by following the embedded types up the chain. + for val.Type() != controllerPtrType { + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + val = val.Field(0) + } + arg = val + } + + vals := i.callable.Call([]reflect.Value{arg}) + return vals[0] +} + +func InterceptorFilter(c *Controller, fc []Filter) { + defer invokeInterceptors(FINALLY, c) + defer func() { + if err := recover(); err != nil { + invokeInterceptors(PANIC, c) + panic(err) + } + }() + + // Invoke the BEFORE interceptors and return early, if we get a result. + invokeInterceptors(BEFORE, c) + if c.Result != nil { + return + } + + fc[0](c, fc[1:]) + invokeInterceptors(AFTER, c) +} + +func invokeInterceptors(when When, c *Controller) { + var ( + app = reflect.ValueOf(c.AppController) + result Result + ) + + for _, intc := range getInterceptors(when, app) { + resultValue := intc.Interceptor.Invoke(app, &intc.Target) + if !resultValue.IsNil() { + result = resultValue.Interface().(Result) + } + if when == BEFORE && result != nil { + c.Result = result + return + } + } + if result != nil { + c.Result = result + } +} + +var interceptors []*Interception + +// InterceptFunc installs a general interceptor. +// This can be applied to any Controller. +// It must have the signature of: +// func example(c *revel.Controller) revel.Result +func InterceptFunc(intc InterceptorFunc, when When, target interface{}) { + interceptors = append(interceptors, &Interception{ + When: when, + function: intc, + callable: reflect.ValueOf(intc), + target: reflect.TypeOf(target), + interceptAll: target == AllControllers, + }) +} + +// InterceptMethod installs an interceptor method that applies to its own Controller. +// func (c AppController) example() revel.Result +// func (c *AppController) example() revel.Result +func InterceptMethod(intc InterceptorMethod, when When) { + methodType := reflect.TypeOf(intc) + if methodType.Kind() != reflect.Func || methodType.NumOut() != 1 || methodType.NumIn() != 1 { + log.Fatalln("Interceptor method should have signature like", + "'func (c *AppController) example() revel.Result' but was", methodType) + } + + interceptors = append(interceptors, &Interception{ + When: when, + method: intc, + callable: reflect.ValueOf(intc), + target: methodType.In(0), + }) +} + +// This item is used to provide a sortable set to be returned to the caller. This ensures calls order is maintained +// +type interceptorItem struct { + Interceptor *Interception + Target reflect.Value + Level int +} +type interceptorItemList []*interceptorItem + +func (a interceptorItemList) Len() int { return len(a) } +func (a interceptorItemList) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a interceptorItemList) Less(i, j int) bool { return a[i].Level < a[j].Level } + +type reverseInterceptorItemList []*interceptorItem + +func (a reverseInterceptorItemList) Len() int { return len(a) } +func (a reverseInterceptorItemList) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a reverseInterceptorItemList) Less(i, j int) bool { return a[i].Level > a[j].Level } +func getInterceptors(when When, val reflect.Value) interceptorItemList { + result := interceptorItemList{} + for _, intc := range interceptors { + if intc.When != when { + continue + } + + level, target := findTarget(val, intc.target) + if intc.interceptAll || target.IsValid() { + result = append(result, &interceptorItem{intc, target, level}) + } + } + + // Before is deepest to highest + if when == BEFORE { + sort.Sort(result) + } else { + // Everything else is highest to deepest + sort.Sort(reverseInterceptorItemList(result)) + } + return result +} + +// Find the value of the target, starting from val and including embedded types. +// Also, convert between any difference in indirection. +// If the target couldn't be found, the returned Value will have IsValid() == false +func findTarget(val reflect.Value, target reflect.Type) (int, reflect.Value) { + // Look through the embedded types (until we reach the *revel.Controller at the top). + valueQueue := []reflect.Value{val} + level := 0 + for len(valueQueue) > 0 { + val, valueQueue = valueQueue[0], valueQueue[1:] + + // Check if val is of a similar type to the target type. + if val.Type() == target { + return level, val + } + if val.Kind() == reflect.Ptr && val.Elem().Type() == target { + return level, val.Elem() + } + if target.Kind() == reflect.Ptr && target.Elem() == val.Type() { + return level, val.Addr() + } + + // If we reached the *revel.Controller and still didn't find what we were + // looking for, give up. + if val.Type() == controllerPtrType { + continue + } + + // Else, add each anonymous field to the queue. + if val.Kind() == reflect.Ptr { + val = val.Elem() + } + + for i := 0; i < val.NumField(); i++ { + if val.Type().Field(i).Anonymous { + valueQueue = append(valueQueue, val.Field(i)) + } + } + level-- + } + + return level, reflect.Value{} +}