github.com/mmatczuk/gohan@v0.0.0-20170206152520-30e45d9bdb69/extension/otto/gohan_core.go (about)

     1  // Copyright (C) 2015 NTT Innovation Institute, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    12  // implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package otto
    17  
    18  import (
    19  	"io/ioutil"
    20  	"strings"
    21  	"fmt"
    22  	"os"
    23  
    24  	"github.com/ddliu/motto"
    25  
    26  	"github.com/cloudwan/gohan/schema"
    27  	"github.com/robertkrimen/otto"
    28  	l "github.com/cloudwan/gohan/log"
    29  	"github.com/cloudwan/gohan/util"
    30  )
    31  
    32  var log = l.NewLogger()
    33  
    34  func init() {
    35  	gohanInit := func(env *Environment) {
    36  		vm := env.VM
    37  		builtins := map[string]interface{}{
    38  			"require": func(call otto.FunctionCall) otto.Value {
    39  				VerifyCallArguments(&call, "require", 1)
    40  				moduleName, err := GetString(call.Argument(0))
    41  				if err != nil {
    42  					ThrowOttoException(&call, err.Error())
    43  				}
    44  				value, err := require(moduleName, vm)
    45  				if err != nil {
    46  					ThrowOttoException(&call, err.Error())
    47  				}
    48  				return value
    49  			},
    50  			"gohan_schemas": func(call otto.FunctionCall) otto.Value {
    51  				VerifyCallArguments(&call, "gohan_schemas", 0)
    52  				manager := schema.GetManager()
    53  				response := []interface{}{}
    54  				for _, schema := range manager.OrderedSchemas() {
    55  					response = append(response, schema)
    56  				}
    57  				value, _ := vm.ToValue(response)
    58  				return value
    59  			},
    60  			"gohan_schema_url": func(call otto.FunctionCall) otto.Value {
    61  				VerifyCallArguments(&call, "gohan_schema_url", 1)
    62  				schemaID, err := GetString(call.Argument(0))
    63  				if err != nil {
    64  					ThrowOttoException(&call, err.Error())
    65  				}
    66  				manager := schema.GetManager()
    67  				schema, ok := manager.Schema(schemaID)
    68  				if !ok {
    69  					ThrowOttoException(&call, unknownSchemaErrorMesssageFormat, schemaID)
    70  				}
    71  				value, _ := vm.ToValue(schema.URL)
    72  				return value
    73  			},
    74  			"gohan_policies": func(call otto.FunctionCall) otto.Value {
    75  				VerifyCallArguments(&call, "gohan_policies", 0)
    76  				manager := schema.GetManager()
    77  				response := []interface{}{}
    78  				for _, policy := range manager.Policies() {
    79  					response = append(response, policy.RawData)
    80  				}
    81  				value, _ := vm.ToValue(response)
    82  				return value
    83  			},
    84  		}
    85  
    86  		for name, object := range builtins {
    87  			vm.Set(name, object)
    88  		}
    89  
    90  		loadNPMModules()
    91  
    92  		err := env.Load("<Gohan built-in exceptions>", `
    93  		function BaseException() {
    94  		  this.fields = ["name", "message"]
    95  		  this.message = "";
    96  		  this.name = "BaseException";
    97  
    98  		  this.toDict = function () {
    99  		    return _.reduce(this.fields, function(resp, field) {
   100  		      resp[field] = this[field];
   101  		      return resp;
   102  		    }, {}, this);
   103  		  };
   104  
   105  		  this.toString = function () {
   106  		    return this.name.concat("(").concat(this.message).concat(")");
   107  		  };
   108  		}
   109  
   110  		function CustomException(msg, code) {
   111  		  BaseException.call(this);
   112  		  this.message = msg;
   113  		  this.name = "CustomException";
   114  		  this.code = code;
   115  		  this.fields.push("code");
   116  		}
   117  		CustomException.prototype = Object.create(BaseException.prototype);
   118  
   119  		function ResourceException(msg, problem) {
   120  		  BaseException.call(this);
   121  		  this.message = msg;
   122  		  this.name = "ResourceException";
   123  		  this.problem = problem;
   124  		  this.fields.push("problem");
   125  		}
   126  		ResourceException.prototype = Object.create(BaseException.prototype);
   127  
   128  		function ExtensionException(msg, inner_exception) {
   129  		  BaseException.call(this);
   130  		  this.message = msg;
   131  		  this.name = "ExtensionException";
   132  		  this.inner_exception = inner_exception;
   133  		  this.fields.push("inner_exception");
   134  		}
   135  		ExtensionException.prototype = Object.create(BaseException.prototype);
   136  		`)
   137  		if err != nil {
   138  			log.Fatal(err)
   139  		}
   140  
   141  		err = env.Load("<Gohan built-ins>", `
   142  		var gohan_handler = {}
   143  		function gohan_register_handler(event_type, func){
   144  		  if(_.isUndefined(gohan_handler[event_type])){
   145  		    gohan_handler[event_type] = [];
   146  		  }
   147  		  gohan_handler[event_type].push(func)
   148  		}
   149  
   150  		function gohan_handle_event(event_type, context){
   151  		  if(_.isUndefined(gohan_handler[event_type])){
   152  		    return;
   153  		  }
   154  
   155  		  for (var i = 0; i < gohan_handler[event_type].length; ++i) {
   156  		    try {
   157  		      var old_module = gohan_log_module_push(event_type);
   158  		      gohan_handler[event_type][i](context);
   159  		      //backwards compatibility
   160  		      if (!_.isUndefined(context.response_code)) {
   161  		        throw new CustomException(context.response, context.response_code);
   162  		      }
   163  		    } catch(e) {
   164  		      if (e instanceof BaseException) {
   165  		        context.exception = e.toDict();
   166  		        context.exception_message = event_type.concat(": ").concat(e.toString());
   167  		      } else {
   168  		        throw e;
   169  		      }
   170  		    } finally {
   171  		      gohan_log_module_restore(old_module);
   172  		    }
   173  		  }
   174  		}
   175  		`)
   176  		if err != nil {
   177  			log.Fatal(err)
   178  		}
   179  	}
   180  	RegisterInit(gohanInit)
   181  }
   182  
   183  func requireFromOtto(moduleName string, vm *otto.Otto) (otto.Value, error) {
   184  	log.Debug(fmt.Sprintf("Loading module %s from otto", moduleName))
   185  	rawModule, errRequire := RequireModule(moduleName)
   186  	if errRequire != nil {
   187  		return otto.UndefinedValue(), errRequire
   188  	}
   189  
   190  	module, errConvert := vm.ToValue(rawModule)
   191  	if errConvert != nil {
   192  		return otto.UndefinedValue(), errConvert
   193  	}
   194  
   195  	return module, nil
   196  }
   197  
   198  func requireFromMotto(moduleName string, vm *motto.Motto) (otto.Value, error) {
   199  	log.Debug(fmt.Sprintf("Loading module %s from motto", moduleName))
   200  	v, err := vm.Require(moduleName, "")
   201  	if err != nil {
   202  		log.Error("Cannot load module %s in Motto, err:%s", moduleName, err.Error())
   203  	}
   204  	return v, err
   205  }
   206  
   207  func require(moduleName string, vm *motto.Motto) (otto.Value, error) {
   208  	value, err := requireFromMotto(moduleName, vm) // NPM
   209  	if err != nil {
   210  		log.Error(fmt.Sprintf("Loading module %s from motto failed: %s, trying to load from otto",
   211  			moduleName, err))
   212  		value, err = requireFromOtto(moduleName, vm.Otto) // Go extensions
   213  	}
   214  
   215  	return value, err
   216  }
   217  
   218  func loadNPMModules() {
   219  	config := util.GetConfig()
   220  	npmPath := config.GetString("extension/npm_path", ".")
   221  	files, _ := ioutil.ReadDir(npmPath + "/node_modules/")
   222  	for _, f := range files {
   223  		if f.IsDir() && !strings.HasPrefix(f.Name(), ".") {
   224  			module, err := motto.FindFileModule(f.Name(), npmPath, nil)
   225  			if err != nil {
   226  				log.Error("Finding module failed %s in %s", err, f.Name())
   227  				break
   228  			}
   229  
   230  			var entryPoint string
   231  			entryPointCandidates := []string {module, module + ".js", module + "/index.js"}
   232  
   233  			for _, candidate := range entryPointCandidates {
   234  				if candidateFile, err := os.Stat(candidate); err == nil && !candidateFile.IsDir() {
   235  					entryPoint = candidate
   236  					break
   237  				}
   238  			}
   239  
   240  			if entryPoint == "" {
   241  				log.Error("Cannot find entry point of %s module", module)
   242  				break
   243  			}
   244  
   245  			loader := motto.CreateLoaderFromFile(entryPoint)
   246  			motto.AddModule(f.Name(), loader)
   247  		}
   248  	}
   249  }