server.go 6.75 KB
Newer Older
1
/* Copyright [1999-2014] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute
2
3
4
5
6
7
8
9
10
11
12
13
14
15

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/

16

17
18
19
package main

import (
20
	"strings"
emepyc's avatar
emepyc committed
21
	"bytes"
22
23
24
	"regexp"
	"strconv"
	"sort"
emepyc's avatar
emepyc committed
25
26
27
	"encoding/json"
	"errors"
	"flag"
28
	"fmt"
emepyc's avatar
emepyc committed
29
	"go/build"
30
	"log"
emepyc's avatar
emepyc committed
31
	"net/http"
32
	"os"
emepyc's avatar
emepyc committed
33
	"os/exec"
34
	"path"
35
	"path/filepath"
36
37
38
39
)

const (
	projectDirName = "github.com/emepyc/guiHive"
40
41
)

emepyc's avatar
emepyc committed
42
43
var (
	port string
44
	isVersion = regexp.MustCompile(`^[0-9]+$`)
emepyc's avatar
emepyc committed
45
46
)

emepyc's avatar
emepyc committed
47
48
func init() {
	flag.StringVar(&port, "port", "8080", "Port to listen (defaults to 8080)")
emepyc's avatar
emepyc committed
49
50
51
	flag.Parse()
}

emepyc's avatar
emepyc committed
52
func checkError(s string, err error, ss ...string) {
emepyc's avatar
emepyc committed
53
	if err != nil {
54
		log.Fatal(s, err, ss)
emepyc's avatar
emepyc committed
55
56
	}
}
57

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// Sortable os.fileInfos by name (-> num)
type sortableFiles []os.FileInfo
func (s sortableFiles) Len () int {
	return len(s)
}
func (s sortableFiles) Less (i, j int) bool {
	iVer, err := strconv.Atoi(s[i].Name())
	checkError(fmt.Sprintf("Dir name %s can't be converted to int", s[i].Name()), err)
	jVer, err := strconv.Atoi(s[j].Name())
	checkError(fmt.Sprintf("Dir name %s can't be converted to int", s[j].Name()), err)
	return iVer < jVer;
}
func (s sortableFiles) Swap (i, j int) {
	s[i], s[j] = s[j], s[i]
}

74
75
76
func version(r *http.Request) string {
	parts := strings.SplitN(r.URL.Path, "/", 4)
	version := parts[2]
77
78
79
	if (isVersion.MatchString(version)) {
		return version
	} else {
80
		path := os.Getenv("GUIHIVE_PROJECTDIR") + "/versions/"
81
82
83
84
85
86
87
88
89
		dir, err := os.Open(path)
		checkError("Can't open dir " + path, err)
		files, err := dir.Readdir(-1)
		checkError("Can't read dir " + path, err)
		sort.Sort(sortableFiles(files))
		version = files[len(files)-1].Name()
		return version
	}
	return ""
90
91
92
93
}

func unknown(w http.ResponseWriter, r *http.Request) {
	version := version(r)
94
	fmt.Fprintln(w, r.URL)
95
96
97
	fmt.Fprintf(w, "version %s is currently not supported by guiHive\n", version)
}

emepyc's avatar
emepyc committed
98
99
func scriptHandler(w http.ResponseWriter, r *http.Request) {
	err := r.ParseForm()
emepyc's avatar
emepyc committed
100
	defer r.Body.Close()
101
	checkError("Can't parse Form: ", err)
emepyc's avatar
emepyc committed
102

emepyc's avatar
emepyc committed
103
104
	debug("METHOD: %s", r.Method)
	debug("URL: %s", r.URL)
emepyc's avatar
emepyc committed
105

emepyc's avatar
emepyc committed
106
107
108
	var outMsg bytes.Buffer
	var errMsg bytes.Buffer

109
	fname := os.Getenv("GUIHIVE_PROJECTDIR") + r.URL.Path
emepyc's avatar
emepyc committed
110
	args, err := json.Marshal(r.Form)
emepyc's avatar
emepyc committed
111
	checkError("Can't Marshal JSON:", err)
emepyc's avatar
emepyc committed
112

emepyc's avatar
emepyc committed
113
	debug("EXECUTING SCRIPT: %s", fname)
114
	debug("ARGS: %s", args)
115
	version := version(r);
116
117
	debug("VERSION: %s", version)
	
118
	versionRootDir := os.Getenv("GUIHIVE_PROJECTDIR") + "/versions/" + version + "/";
119
120
121
122
123
124
125
	ehiveRootDir := versionRootDir + "/ensembl-hive"
	ehiveRootLib := ehiveRootDir + "/modules"
	guihiveRootLib := versionRootDir + "/scripts/lib"
	newPerl5Lib  := addPerl5Lib(ehiveRootLib + ":" + guihiveRootLib)

	debug("EHIVE_ROOT_DIR: %s", ehiveRootDir)
	debug("NEW_PERL5LIB: %s", newPerl5Lib)
emepyc's avatar
emepyc committed
126
	cmd := exec.Command(fname, string(args))
127
128
129
	cmd.Env = make([]string,0)
	cmd.Env = append(cmd.Env, "PERL5LIB=" + newPerl5Lib)
	cmd.Env = append(cmd.Env, "EHIVE_ROOT_DIR=" + ehiveRootDir)
130
	cmd.Env = append(cmd.Env, "GUIHIVE_BASEDIR=" + versionRootDir)
131
132
	cmd.Env = append(cmd.Env, "PATH=" + os.Getenv("PATH"))

emepyc's avatar
emepyc committed
133
134
135
136
	cmd.Stdout = &outMsg
	cmd.Stderr = &errMsg

	if err := cmd.Start(); err != nil {
emepyc's avatar
emepyc committed
137
		log.Println("Error Starting Command: ", err)
emepyc's avatar
emepyc committed
138
139
	}
	if err := cmd.Wait(); err != nil {
emepyc's avatar
emepyc committed
140
		log.Println("Error Executing Command: ", err)
emepyc's avatar
emepyc committed
141
	}
emepyc's avatar
emepyc committed
142

143
144
	debug("OUTMSG: %s", outMsg.Bytes())
	debug("ERRMSG: %s", errMsg.Bytes())
145
	fmt.Fprintln(w, string(outMsg.Bytes()))
emepyc's avatar
emepyc committed
146

147
148
}

149
150
func pathExists(name string) bool {
	_, err := os.Stat(name)
151
152
153
	if os.IsNotExist(err) {
		return false
	}
154
155
156
157
158
	return err == nil
}

func guessProjectDir() (string, error) {
	// First, we try to find the project dir in the working directory
159
	serverPath := os.Args[0]
160
	serverDir := filepath.Dir(serverPath)
emepyc's avatar
emepyc committed
161
	pathToIndex := serverDir + "/../index.html"
162
	absPathToIndex, err := filepath.Abs(pathToIndex)
163
	if err != nil {
emepyc's avatar
emepyc committed
164
		debug("ABSPATHTOINDEX: %s\n", absPathToIndex)
165
166
		return "", err
	}
167
	if pathExists(absPathToIndex) {
emepyc's avatar
emepyc committed
168
		return path.Clean(absPathToIndex + "/.."), nil
169
170
171
	}
	for _, srcdir := range build.Default.SrcDirs() {
		dirName := path.Join(srcdir, projectDirName)
emepyc's avatar
emepyc committed
172
		fmt.Println("DIRNAME: ", dirName)
173
174
175
176
177
178
179
180
181
		if pathExists(dirName) {
			return dirName, nil
		}
	}
	return "", errors.New("Project directory not found")
}

func setEnvVar() error {
	projectDirectory, err := guessProjectDir()
182
183
184
	if err != nil {
		return err
	}
185
186
187
188
189
	//GUIHIVE_PROJECTDIR
	if err := os.Setenv("GUIHIVE_PROJECTDIR", projectDirectory+"/"); err != nil {
		return err
	}
	debug("PROJECT_DIRECTORY: %s\n", os.Getenv("GUIHIVE_PROJECTDIR"))
190
191

	// PER5LIB
192
193
	newPerl5Lib := addPerl5Lib(path.Clean(projectDirectory + "/scripts/lib"))
	err = setPerl5Lib(newPerl5Lib)
194
	if (err != nil) {
195
196
197
198
199
200
		return err
	}

	return nil
}

201
func addPerl5Lib (newDir string) string {
202
203
204
205
	perl5lib := os.Getenv("PERL5LIB")
	if perl5lib == "" {
		perl5lib = newDir
	} else {
206
		perl5lib = newDir + ":" + perl5lib
207
	}
208
209
210
211
	return perl5lib
}

func setPerl5Lib (perl5lib string) error {
212
213
214
215
216
217
218
219
220
	err := os.Setenv("PERL5LIB", perl5lib)
	if err != nil {
		return err
	}
	debug("PERL5LIB: %s\n", os.Getenv("PERL5LIB"))

	return nil
}

221
func main() {
222
223
224

	//  Fix environmental variables
	errV := setEnvVar()
emepyc's avatar
emepyc committed
225
	checkError("Problem setting environmental variables: ", errV)
226

227
	relPath := os.Getenv("GUIHIVE_PROJECTDIR")
228

emepyc's avatar
emepyc committed
229
	http.Handle("/", http.FileServer(http.Dir(relPath)))
230
	http.HandleFunc("/versions/", unknown)
emepyc's avatar
emepyc committed
231
232
233
	http.Handle("/styles/", http.FileServer(http.Dir(relPath)))
	http.Handle("/javascript/", http.FileServer(http.Dir(relPath)))
	http.Handle("/images/", http.FileServer(http.Dir(relPath)))
emepyc's avatar
emepyc committed
234
	http.HandleFunc("/scripts/", scriptHandler)
235
236
237
238
239
240

	versionRootDir := relPath + "/versions/"
	dir, terr := os.Open(versionRootDir)
	checkError("Can't open dir " + versionRootDir, terr)
	files, terr := dir.Readdir(-1)
	for _, verdir := range files {
241
242
243
244
245
		if ((verdir.Mode() & os.ModeSymlink) != 0) {
			targetF, _ := os.Lstat(versionRootDir + verdir.Name())
			debug("Found a symlink from %s to version %s", verdir.Name(), targetF.Name())
			verdir = targetF
		} else if (verdir.IsDir()) {
246
247
248
249
250
251
252
			debug("Found eHive version %s", verdir.Name())
			http.Handle(fmt.Sprintf("/versions/%s/", verdir.Name()), http.FileServer(http.Dir(relPath)))
			http.Handle(fmt.Sprintf("/versions/%s/javascript/", verdir.Name()), http.FileServer(http.Dir(relPath)))
			http.HandleFunc(fmt.Sprintf("/versions/%s/scripts/", verdir.Name()), scriptHandler)
		}
	}

emepyc's avatar
emepyc committed
253
254
	debug("Listening to port: %s", port)
	err := http.ListenAndServe(":"+port, nil)
255
	checkError("ListenAndServe ", err)
256
}