func (j *Job) Run() { // If the job panics, just print a stack trace. // Don't let the whole process die. defer func() { if err := recover(); err != nil { if revelError := revel.NewErrorFromPanic(err); revelError != nil { glog.Error(err, "\n", revelError.Stack) } else { glog.Error(err, "\n", string(debug.Stack())) } } }() if !selfConcurrent { j.running.Lock() defer j.running.Unlock() } if workPermits != nil { workPermits <- struct{}{} defer func() { <-workPermits }() } atomic.StoreUint32(&j.status, 1) defer atomic.StoreUint32(&j.status, 0) j.inner.Run() }
// This function handles a panic in an action invocation. // It cleans up the stack trace, logs it, and displays an error page. func handleInvocationPanic(c *Controller, err interface{}) { error := NewErrorFromPanic(err) if error == nil && DevMode { // Only show the sensitive information in the debug stack trace in development mode, not production glog.Error(err, "\n", string(debug.Stack())) c.Response.Out.WriteHeader(500) c.Response.Out.Write(debug.Stack()) return } glog.Error(err, "\n", error.Stack) c.Result = c.RenderError(error) }
func (r *RenderTemplateResult) Apply(req *Request, resp *Response) { // Handle panics when rendering templates. defer func() { if err := recover(); err != nil { glog.Error(err) PlaintextErrorResult{fmt.Errorf("Template Execution Panic in %s:\n%s", r.Template.Name(), err)}.Apply(req, resp) } }() chunked := Config.BoolDefault("results.chunked", false) // In a prod mode, write the status, render, and hope for the best. // (In a dev mode, always render to a temporary buffer first to avoid having // error pages distorted by HTML already written) defaultContentType := FormatToContentType(req.Format) if chunked && !DevMode { resp.WriteHeader(http.StatusOK, defaultContentType) r.render(req, resp, resp.Out) return } // Render the template into a temporary buffer, to see if there was an error // rendering the template. If not, then copy it into the response buffer. // Otherwise, template render errors may result in unpredictable HTML (and // would carry a 200 status code) var b bytes.Buffer r.render(req, resp, &b) if !chunked { resp.Out.Header().Set("Content-Length", strconv.Itoa(b.Len())) } resp.WriteHeader(http.StatusOK, defaultContentType) b.WriteTo(resp.Out) }
func convertMemcacheError(err error) error { switch err { case memcache.ErrCacheMiss: return ErrCacheMiss case memcache.ErrNotStored: return ErrNotStored } glog.Error("revel/cache:", err) return err }
// Parse the output of the "go build" command. // Return a detailed Error. func newCompileError(output []byte) *revel.Error { errorMatch := regexp.MustCompile(`(?m)^([^:#]+):(\d+):(\d+:)? (.*)$`). FindSubmatch(output) if errorMatch == nil { glog.Error("Failed to parse build errors:\n", string(output)) return &revel.Error{ SourceType: "Go code", Title: "Go Compilation Error", Description: "See console for build error.", } } // Read the source for the offending file. var ( relFilename = string(errorMatch[1]) // e.g. "src/revel/sample/app/controllers/app.go" absFilename, _ = filepath.Abs(relFilename) line, _ = strconv.Atoi(string(errorMatch[2])) description = string(errorMatch[4]) compileError = &revel.Error{ SourceType: "Go code", Title: "Go Compilation Error", Path: relFilename, Description: description, Line: line, } ) fileStr, err := revel.ReadLines(absFilename) if err != nil { compileError.MetaError = absFilename + ": " + err.Error() glog.Error(compileError.MetaError) return compileError } compileError.SourceLines = fileStr return compileError }
func (c InMemoryCache) Get(key string, ptrValue interface{}) error { value, found := c.Cache.Get(key) if !found { return ErrCacheMiss } v := reflect.ValueOf(ptrValue) if v.Type().Kind() == reflect.Ptr && v.Elem().CanSet() { v.Elem().Set(reflect.ValueOf(value)) return nil } err := fmt.Errorf("revel/cache: attempt to get %s, but can not set value %v", key, v) glog.Error(err) return err }
func importPathFromPath(root string) string { for _, gopath := range filepath.SplitList(build.Default.GOPATH) { srcPath := filepath.Join(gopath, "src") if strings.HasPrefix(root, srcPath) { return filepath.ToSlash(root[len(srcPath)+1:]) } } srcPath := filepath.Join(build.Default.GOROOT, "src", "pkg") if strings.HasPrefix(root, srcPath) { glog.Warning("Code path should be in GOPATH, but is in GOROOT: ", root) return filepath.ToSlash(root[len(srcPath)+1:]) } glog.Error("Unexpected! Code path is not in GOPATH: ", root) return "" }
// Render a template corresponding to the calling Controller method. // Arguments will be added to c.RenderArgs prior to rendering the template. // They are keyed on their local identifier. // // For example: // // func (c Users) ShowUser(id int) revel.Result { // user := loadUser(id) // return c.Render(user) // } // // This action will render views/Users/ShowUser.html, passing in an extra // key-value "user": (User). // // Content negotiation // // The template selected depends on the request's format (html, json, xml, txt), // (which is derived from the Accepts header). For example, if Request.Format // was "json", then the above example would look for the // views/Users/ShowUser.json template instead. // // If no template is found and the format is one of "json" or "xml", // then Render will instead serialize the first argument into that format. func (c *Controller) Render(extraRenderArgs ...interface{}) Result { templatePath := c.Name + "/" + c.MethodType.Name + "." + c.Request.Format // Get the calling function name. _, _, line, ok := runtime.Caller(1) if !ok { glog.Error("Failed to get Caller information") } // If not HTML, first check if the template is present. template, err := MainTemplateLoader.Template(templatePath) // If not, and there is an arg, serialize that if it's xml or json. if template == nil { if len(extraRenderArgs) > 0 { switch c.Request.Format { case "xml": return c.RenderXml(extraRenderArgs[0]) case "json": return c.RenderJson(extraRenderArgs[0]) } } // Else, render a 404 error saying we couldn't find the template. return c.NotFound(err.Error()) } // Get the extra RenderArgs passed in. if renderArgNames, ok := c.MethodType.RenderArgNames[line]; ok { if len(renderArgNames) == len(extraRenderArgs) { for i, extraRenderArg := range extraRenderArgs { c.RenderArgs[renderArgNames[i]] = extraRenderArg } } else { glog.Errorln(len(renderArgNames), "RenderArg names found for", len(extraRenderArgs), "extra RenderArgs") } } else { glog.Errorln("No RenderArg names found for Render call on line", line, "(Method", c.MethodType.Name, ")") } return &RenderTemplateResult{ Template: template, RenderArgs: c.RenderArgs, } }
// Prepares the route to be used in matching. func NewRoute(method, path, action, fixedArgs string) (r *Route) { // Handle fixed arguments argsReader := strings.NewReader(fixedArgs) csv := csv.NewReader(argsReader) fargs, err := csv.Read() if err != nil && err != io.EOF { glog.Errorf("Invalid fixed parameters (%v): for string '%v'", err.Error(), fixedArgs) } r = &Route{ Method: strings.ToUpper(method), Path: path, Action: action, FixedParams: fargs, } // URL pattern // TODO: Support non-absolute paths if !strings.HasPrefix(r.Path, "/") { glog.Error("Absolute URL required.") return } // Handle embedded arguments // Convert path arguments with unspecified regexes to standard form. // e.g. "/customer/{id}" => "/customer/{<[^/]+>id} normPath := nakedPathParamRegex.ReplaceAllStringFunc(r.Path, func(m string) string { var argMatches []string = nakedPathParamRegex.FindStringSubmatch(m) return "{<[^/]+>" + argMatches[1] + "}" }) // Go through the arguments r.args = make([]*arg, 0, 3) for i, m := range argsPattern.FindAllStringSubmatch(normPath, -1) { r.args = append(r.args, &arg{ name: string(m[2]), index: i, constraint: regexp.MustCompile(string(m[1])), }) } // Now assemble the entire path regex, including the embedded parameters. // e.g. /app/{<[^/]+>id} => /app/(?P<id>[^/]+) pathPatternStr := argsPattern.ReplaceAllStringFunc(normPath, func(m string) string { var argMatches []string = argsPattern.FindStringSubmatch(m) return "(?P<" + argMatches[2] + ">" + argMatches[1] + ")" }) r.pathPattern = regexp.MustCompile(pathPatternStr + "$") // Handle action var actionPatternStr string = strings.Replace(r.Action, ".", `\.`, -1) for _, arg := range r.args { var argName string = "{" + arg.name + "}" if argIndex := strings.Index(actionPatternStr, argName); argIndex != -1 { actionPatternStr = strings.Replace(actionPatternStr, argName, "(?P<"+arg.name+">"+arg.constraint.String()+")", -1) } } r.actionPattern = regexp.MustCompile(actionPatternStr) return }
func (c MemcachedCache) Flush() error { err := errors.New("revel/cache: can not flush memcached.") glog.Error(err) return err }
// Build the app: // 1. Generate the the main.go file. // 2. Run the appropriate "go build" command. // Requires that revel.Init has been called previously. // Returns the path to the built binary, and an error if there was a problem building it. func Build() (app *App, compileError *revel.Error) { start := time.Now() // First, clear the generated files (to avoid them messing with ProcessSource). cleanSource("tmp", "routes") sourceInfo, compileError := ProcessSource(revel.CodePaths) if compileError != nil { return nil, compileError } // Add the db.import to the import paths. if dbImportPath, found := revel.Config.String("db.import"); found { sourceInfo.InitImportPaths = append(sourceInfo.InitImportPaths, dbImportPath) } // Generate two source files. templateArgs := map[string]interface{}{ "Controllers": sourceInfo.ControllerSpecs(), "ValidationKeys": sourceInfo.ValidationKeys, "ImportPaths": calcImportAliases(sourceInfo), "TestSuites": sourceInfo.TestSuites(), } genSource("tmp", "main.go", MAIN, templateArgs) genSource("routes", "routes.go", ROUTES, templateArgs) // Read build config. buildTags := revel.Config.StringDefault("build.tags", "") // Build the user program (all code under app). // It relies on the user having "go" installed. goPath, err := exec.LookPath("go") if err != nil { glog.Fatalf("Go executable not found in PATH.") } pkg, err := build.Default.Import(revel.ImportPath, "", build.FindOnly) if err != nil { glog.Fatalln("Failure importing", revel.ImportPath) } binName := filepath.Join(pkg.BinDir, filepath.Base(revel.BasePath)) if runtime.GOOS == "windows" { binName += ".exe" } gotten := make(map[string]struct{}) for { buildCmd := exec.Command(goPath, "build", "-i", "-tags", buildTags, "-o", binName, path.Join(revel.ImportPath, "app", "tmp")) glog.V(1).Infoln("Exec:", buildCmd.Args) output, err := buildCmd.CombinedOutput() // If the build succeeded, we're done. if err == nil { glog.Infof("Build took %s", time.Since(start)) return NewApp(binName), nil } glog.Error(string(output)) // See if it was an import error that we can go get. matches := importErrorPattern.FindStringSubmatch(string(output)) if matches == nil { return nil, newCompileError(output) } // Ensure we haven't already tried to go get it. pkgName := matches[1] if _, alreadyTried := gotten[pkgName]; alreadyTried { return nil, newCompileError(output) } gotten[pkgName] = struct{}{} // Execute "go get <pkg>" getCmd := exec.Command(goPath, "get", pkgName) glog.V(1).Infoln("Exec:", getCmd.Args) getOutput, err := getCmd.CombinedOutput() if err != nil { glog.Error(string(getOutput)) return nil, newCompileError(output) } // Success getting the import, attempt to build again. } glog.Fatal("Not reachable") return nil, nil }