Esempio n. 1
0
// findSrcPaths uses the "go/build" package to find the source root for Revel
// and the app.
func findSrcPaths(importPath string) (revelSourcePath, appSourcePath string) {
	var (
		gopaths = filepath.SplitList(build.Default.GOPATH)
		goroot  = build.Default.GOROOT
	)

	if len(gopaths) == 0 {
		glog.Fatal("GOPATH environment variable is not set. ",
			"Please refer to http://golang.org/doc/code.html to configure your Go environment.")
	}

	if ContainsString(gopaths, goroot) {
		glog.Fatalf("GOPATH (%s) must not include your GOROOT (%s). "+
			"Please refer to http://golang.org/doc/code.html to configure your Go environment.",
			gopaths, goroot)
	}

	appPkg, err := build.Import(importPath, "", build.FindOnly)
	if err != nil {
		glog.Fatalln("Failed to import", importPath, "with error:", err)
	}

	revelPkg, err := build.Import(REVEL_IMPORT_PATH, "", build.FindOnly)
	if err != nil {
		glog.Fatalln("Failed to find Revel with error:", err)
	}

	return revelPkg.SrcRoot, appPkg.SrcRoot
}
Esempio n. 2
0
// genSource renders the given template to produce source code, which it writes
// to the given directory and file.
func genSource(dir, filename, templateSource string, args map[string]interface{}) {
	sourceCode := revel.ExecuteTemplate(
		template.Must(template.New("").Parse(templateSource)),
		args)

	// Create a fresh dir.
	tmpPath := filepath.Join(revel.AppPath, dir)
	err := os.RemoveAll(tmpPath)
	if err != nil {
		glog.Errorln("Failed to remove dir:", err)
	}
	err = os.Mkdir(tmpPath, 0777)
	if err != nil {
		glog.Fatalln("Failed to make tmp directory:", err)
	}

	// Create the file
	file, err := os.Create(filepath.Join(tmpPath, filename))
	defer file.Close()
	if err != nil {
		glog.Fatalln("Failed to create file:", err)
	}
	_, err = file.WriteString(sourceCode)
	if err != nil {
		glog.Fatalln("Failed to write to file:", err)
	}
}
Esempio n. 3
0
// Run the harness, which listens for requests and proxies them to the app
// server, which it runs and rebuilds as necessary.
func (h *Harness) Run() {
	revel.ConfigureLogging()
	watcher = revel.NewWatcher()
	watcher.Listen(h, revel.CodePaths...)

	go func() {
		addr := fmt.Sprintf("%s:%d", revel.HttpAddr, revel.HttpPort)
		glog.Infof("Listening on %s", addr)

		var err error
		if revel.HttpSsl {
			err = http.ListenAndServeTLS(addr, revel.HttpSslCert,
				revel.HttpSslKey, h)
		} else {
			err = http.ListenAndServe(addr, h)
		}
		if err != nil {
			glog.Fatalln("Failed to start reverse proxy:", err)
		}
	}()

	// Kill the app on signal.
	ch := make(chan os.Signal)
	signal.Notify(ch, os.Interrupt, os.Kill)
	<-ch
	if h.app != nil {
		h.app.Kill()
	}
	os.Exit(1)
}
Esempio n. 4
0
// ConfigureLogging applies the configuration in revel.Config to the glog flags.
// Logger flags specified explicitly on the command line are not changed.
func ConfigureLogging() {
	// Get the flags specified on the command line.
	specifiedFlags := make(map[string]struct{})
	flag.Visit(func(f *flag.Flag) { specifiedFlags[f.Name] = struct{}{} })

	// For each logger option in app.conf..
	var err error
	for _, option := range Config.Options("log.") {
		val, _ := Config.String(option)
		switch flagname := option[len("log."):]; flagname {
		case "v", "vmodule", "logtostderr", "alsologtostderr", "stderrthreshold", "log_dir":
			// If it was specified on the command line, don't set it from app.conf
			if _, ok := specifiedFlags[flagname]; ok {
				continue
			}

			// Look up the flag and set it.
			// If it's log_dir, make it into an absolute path and creat it if necessary.
			if flagname == "log_dir" {
				if val, err = filepath.Abs(val); err != nil {
					glog.Fatalln("Failed to get absolute path to log_dir:", err)
				}
				os.MkdirAll(val, 0777) // Create the log dir if it doesn't already exist.
			}
			if err = flag.Set(flagname, val); err != nil {
				glog.Fatalf("Failed to set glog option for %s=%s: %s", flagname, val, err)
			}
		case "maxsize":
			if glog.MaxSize, err = humanize.ParseBytes(val); err != nil {
				glog.Fatalf("Failed to parse log.MaxSize=%s: %s", val, err)
			}
		}
	}
}
Esempio n. 5
0
// Load mime-types.conf on init.
func LoadMimeConfig() {
	var err error
	mimeConfig, err = LoadConfig("mime-types.conf")
	if err != nil {
		glog.Fatalln("Failed to load mime type config:", err)
	}
}
Esempio n. 6
0
// Terminate the app server if it's running.
func (cmd AppCmd) Kill() {
	if cmd.Cmd != nil && (cmd.ProcessState == nil || !cmd.ProcessState.Exited()) {
		glog.V(1).Infoln("Killing revel server pid", cmd.Process.Pid)
		err := cmd.Process.Kill()
		if err != nil {
			glog.Fatalln("Failed to kill revel server:", err)
		}
	}
}
Esempio n. 7
0
// Run the server.
// This is called from the generated main file.
// If port is non-zero, use that.  Else, read the port from app.conf.
func Run(port int) {
	address := HttpAddr
	if port == 0 {
		port = HttpPort
	}

	MainTemplateLoader = NewTemplateLoader(TemplatePaths)

	// The "watch" config variable can turn on and off all watching.
	// (As a convenient way to control it all together.)
	if Config.BoolDefault("watch", true) {
		MainWatcher = NewWatcher()
		Filters = append([]Filter{WatchFilter}, Filters...)
	}

	// If desired (or by default), create a watcher for templates and routes.
	// The watcher calls Refresh() on things on the first request.
	if MainWatcher != nil && Config.BoolDefault("watch.templates", true) {
		MainWatcher.Listen(MainTemplateLoader, TemplatePaths...)
	} else {
		MainTemplateLoader.Refresh()
	}

	Server = &http.Server{
		Addr:    fmt.Sprintf("%s:%d", address, port),
		Handler: http.HandlerFunc(handle),
	}

	runStartupHooks()

	go func() {
		time.Sleep(100 * time.Millisecond)
		fmt.Printf("Listening on port %d...\n", port)
	}()

	if HttpSsl {
		glog.Fatalln("Failed to listen:",
			Server.ListenAndServeTLS(HttpSslCert, HttpSslKey))
	} else {
		glog.Fatalln("Failed to listen:", Server.ListenAndServe())
	}
}
Esempio n. 8
0
// LoadModules looks through Config for all modules that need to be loaded,
// adding their controllers and templates to the revel application.
func LoadModules() {
	for _, key := range Config.Options("module.") {
		moduleImportPath := Config.StringDefault(key, "")
		if moduleImportPath == "" {
			continue
		}

		modulePath, err := ResolveImportPath(moduleImportPath)
		if err != nil {
			glog.Fatalln("Failed to load module.  Import of", moduleImportPath, "failed:", err)
		}
		addModule(key[len("module."):], moduleImportPath, modulePath)
	}
}
Esempio n. 9
0
// Start the app server, and wait until it is ready to serve requests.
func (cmd AppCmd) Start() error {
	listeningWriter := startupListeningWriter{os.Stdout, make(chan bool)}
	cmd.Stdout = listeningWriter
	glog.V(1).Infoln("Exec app:", cmd.Path, cmd.Args)
	if err := cmd.Cmd.Start(); err != nil {
		glog.Fatalln("Error running:", err)
	}

	select {
	case <-cmd.waitChan():
		return errors.New("revel/harness: app died")

	case <-time.After(30 * time.Second):
		cmd.Kill()
		return errors.New("revel/harness: app timed out")

	case <-listeningWriter.notifyReady:
		return nil
	}
	panic("Impossible")
}
Esempio n. 10
0
// Refresh scans the views directory and parses all templates using the
// configured TemplateEngines.  If a template fails to parse, the error is set
// on the loader (and returned).
func (loader *TemplateLoader) Refresh() *Error {
	glog.V(1).Infof("Refreshing templates from %s", loader.paths)
	loader.compileError = nil
	loader.templatePaths = map[string]string{}

	// Set the template delimiters for the project if present, then split into left
	// and right delimiters around a space character
	var splitDelims []string
	if delims := Config.StringDefault("template.delimiters", ""); delims != "" {
		splitDelims = strings.Split(delims, " ")
		if len(splitDelims) != 2 {
			glog.Fatalln("app.conf: Incorrect format for template.delimiters")
		}
	}

	loader.defaultEngine = NewTextTemplateEngine()
	loader.engines = map[string]TemplateEngine{
		".html": NewHtmlTemplateEngine(),
		".xml":  NewHtmlTemplateEngine(),
		".json": NewTextTemplateEngine(),
		".txt":  NewTextTemplateEngine(),
	}

	// Walk through the template loader's paths and pass each template to the
	// appropriate engine.
	for _, basePath := range loader.paths {

		// Walk only returns an error if the template loader is completely unusable
		// (namely, if one of the TemplateFuncs does not have an acceptable signature).
		funcErr := filepath.Walk(basePath, func(path string, info os.FileInfo, err error) (walkErr error) {
			defer func() {
				if err := recover(); err != nil {
					walkErr = &Error{
						Title:       "Panic (Template Loader)",
						Description: fmt.Sprintln(err),
					}
				}
			}()

			if err != nil {
				glog.Errorln("error walking templates:", err)
				return nil
			}

			// Walk into directories.
			if info.IsDir() {
				if !loader.WatchDir(info.Name()) {
					return filepath.SkipDir
				}
				return nil
			}

			if !loader.WatchFile(info.Name()) {
				return nil
			}

			// Convert template names to use forward slashes, even on Windows.
			templateName := path[len(basePath)+1:]
			if os.PathSeparator == '\\' {
				templateName = strings.Replace(templateName, "\\", "/", -1)
			}

			// If we already loaded a template of this name, skip it.
			if _, ok := loader.templatePaths[templateName]; ok {
				return nil
			}
			loader.templatePaths[templateName] = path

			fileBytes, err := ioutil.ReadFile(path)
			if err != nil {
				glog.Errorln("Failed reading file:", path)
				return nil
			}

			ext := filepath.Ext(templateName)
			engine, ok := loader.engines[ext]
			if !ok {
				engine = loader.defaultEngine
			}

			// If alternate delimiters set for the project, change them for this template.
			if splitDelims != nil {
				if strings.HasPrefix(path, ViewsPath) {
					engine.Delims(splitDelims[0], splitDelims[1])
				} else {
					engine.Delims("", "")
				}
			}

			err = engine.Parse(templateName, string(fileBytes))

			// Store / report the first error encountered.
			if err != nil && loader.compileError == nil {
				_, line, description := parseTemplateError(err)
				loader.compileError = &Error{
					Title:       "Template Compilation Error",
					Path:        templateName,
					Description: description,
					Line:        line,
					SourceLines: strings.Split(string(fileBytes), "\n"),
				}
				glog.Errorf("Template compilation error (In %s around line %d):\n%s",
					templateName, line, description)
			}

			return nil
		})

		// If there was an error with the Funcs, set it and return immediately.
		if funcErr != nil {
			loader.compileError = funcErr.(*Error)
			return loader.compileError
		}
	}
	return loader.compileError
}
Esempio n. 11
0
// Run the app server inline.  Never returns.
func (cmd AppCmd) Run() {
	glog.V(1).Infoln("Exec app:", cmd.Path, cmd.Args)
	if err := cmd.Cmd.Run(); err != nil {
		glog.Fatalln("Error running:", err)
	}
}
Esempio n. 12
0
// Init initializes Revel -- it provides paths for getting around the app.
//
// Params:
//   mode - the run mode, which determines which app.conf settings are used.
//   importPath - the Go import path of the application.
//   srcPath - the path to the source directory, containing Revel and the app.
//     If not specified (""), then a functioning Go installation is required.
func Init(mode, importPath, srcPath string) {
	// Ignore trailing slashes.
	ImportPath = strings.TrimRight(importPath, "/")
	SourcePath = srcPath
	RunMode = mode

	// If the SourcePath is not specified, find it using build.Import.
	var revelSourcePath string // may be different from the app source path
	if SourcePath == "" {
		revelSourcePath, SourcePath = findSrcPaths(importPath)
	} else {
		// If the SourcePath was specified, assume both Revel and the app are within it.
		SourcePath = filepath.Clean(SourcePath)
		revelSourcePath = SourcePath
		packaged = true
	}

	RevelPath = filepath.Join(revelSourcePath, filepath.FromSlash(REVEL_IMPORT_PATH))
	BasePath = filepath.Join(SourcePath, filepath.FromSlash(importPath))
	AppPath = filepath.Join(BasePath, "app")
	ViewsPath = filepath.Join(AppPath, "views")

	CodePaths = []string{AppPath}

	ConfPaths = []string{
		filepath.Join(BasePath, "conf"),
		filepath.Join(RevelPath, "conf"),
	}

	TemplatePaths = []string{
		ViewsPath,
		filepath.Join(RevelPath, "templates"),
	}

	// Load app.conf
	var err error
	Config, err = LoadConfig("app.conf")
	if err != nil || Config == nil {
		glog.Fatalln("Failed to load app.conf:", err)
	}
	// Ensure that the selected runmode appears in app.conf.
	// If empty string is passed as the mode, treat it as "DEFAULT"
	if mode == "" {
		mode = config.DEFAULT_SECTION
	}
	if !Config.HasSection(mode) {
		glog.Fatalln("app.conf: No mode found:", mode)
	}
	Config.SetSection(mode)

	// Configure properties from app.conf
	DevMode = Config.BoolDefault("mode.dev", false)
	HttpPort = Config.IntDefault("http.port", 9000)
	HttpAddr = Config.StringDefault("http.addr", "")
	HttpSsl = Config.BoolDefault("http.ssl", false)
	HttpSslCert = Config.StringDefault("http.sslcert", "")
	HttpSslKey = Config.StringDefault("http.sslkey", "")
	if HttpSsl {
		if HttpSslCert == "" {
			log.Fatalln("No http.sslcert provided.")
		}
		if HttpSslKey == "" {
			log.Fatalln("No http.sslkey provided.")
		}
	}

	AppName = Config.StringDefault("app.name", "(not set)")
	CookiePrefix = Config.StringDefault("cookie.prefix", "REVEL")
	CookieHttpOnly = Config.BoolDefault("cookie.httponly", false)
	CookieSecure = Config.BoolDefault("cookie.secure", false)
	if secretStr := Config.StringDefault("app.secret", ""); secretStr != "" {
		secretKey = []byte(secretStr)
	}

	Initialized = true
}
Esempio n. 13
0
// 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
}