/src/fan/Dev.fan
Unknown | 203 lines | 176 code | 27 blank | 0 comment | 0 complexity | cee3c91711a85b22c511ef3707d66953 MD5 | raw file
1// 2// Copyright (c) 2011, Andy Frank 3// Licensed under the MIT License 4// 5// History: 6// 6 Jun 2011 Andy Frank Creation 7// 8 9using concurrent 10using util 11using web 12using wisp 13 14////////////////////////////////////////////////////////////////////////// 15// DevRestarter 16////////////////////////////////////////////////////////////////////////// 17 18** DevRestarter 19const class DevRestarter : Actor 20{ 21 new make(ActorPool p, |This| f) : super(p) 22 { 23 f(this) 24 } 25 26 ** Check if pods have been modified. 27 Void checkPods() { send("verify").get(30sec) } 28 29 override Obj? receive(Obj? msg) 30 { 31 if (msg == "verify") 32 { 33 map := Actor.locals["ts"] as Pod:DateTime 34 if (map == null) 35 { 36 startProc 37 Actor.locals["ts"] = update 38 } 39 else if (podsModified(map)) 40 { 41 log.info("Pods modified, restarting WispService") 42 stopProc; startProc; Actor.sleep(2sec) 43 Actor.locals["ts"] = update 44 } 45 } 46 return null 47 } 48 49 ** Update pod modified timestamps. 50 private Pod:DateTime update() 51 { 52 map := Pod:DateTime[:] 53 Pod.list.each |p| { map[p] = podFile(p).modified } 54 log.debug("Update pod timestamps ($map.size pods)") 55 return map 56 } 57 58 ** Return pod file for this Pod. 59 private File podFile(Pod pod) 60 { 61 Env? env := Env.cur 62 file := env.workDir + `_doesnotexist_` 63 64 // walk envs looking for pod file 65 while (!file.exists && env != null) 66 { 67 if (env is PathEnv) 68 { 69 ((PathEnv)env).path.eachWhile |p| 70 { 71 file = p + `lib/fan/${pod.name}.pod` 72 return file.exists ? true : null 73 } 74 } 75 else 76 { 77 file = env.workDir + `lib/fan/${pod.name}.pod` 78 } 79 env = env.parent 80 } 81 82 // verify exists and return 83 if (!file.exists) throw Err("Pod file not found $pod.name") 84 return file 85 } 86 87 ** Return true if any pods have been modified since startup. 88 private Bool podsModified(Pod:DateTime map) 89 { 90 true == Pod.list.eachWhile |p| 91 { 92 if (podFile(p).modified > map[p]) 93 { 94 log.debug("$p.name pod has been modified") 95 return true 96 } 97 return null 98 } 99 } 100 101 ** Start DraftMod process. 102 private Void startProc() 103 { 104 home := Env.cur.homeDir.osPath 105 args := ["java", "-cp", "${home}/lib/java/sys.jar", "-Dfan.home=$home", 106 "fanx.tools.Fan", "draft", "-port", "$port", "-prod"] 107 if (props != null) args.addAll(["-props", props.osPath]) 108 args.add(type.qname) 109 proc := Process(args).run 110 111 Actor.locals["proc"] = proc 112 log.debug("Start external process") 113 } 114 115 ** Stop DraftMod process. 116 private Void stopProc() 117 { 118 proc := Actor.locals["proc"] as Process 119 if (proc == null) return 120 proc.kill 121 log.debug("Stop external process") 122 } 123 124 const Type type 125 const Int port 126 const File? props 127 const Log log := Log.get("draft") 128} 129 130////////////////////////////////////////////////////////////////////////// 131// DevMod 132////////////////////////////////////////////////////////////////////////// 133 134** DevMod 135const class DevMod : WebMod 136{ 137 ** Constructor. 138 new make(DevRestarter r) 139 { 140 this.restarter = r 141 this.port = r.port 142 } 143 144 ** Target port to proxy requests to. 145 const Int port 146 147 ** DevRestarter instance. 148 const DevRestarter restarter 149 150 override Void onService() 151 { 152 // check pods 153 restarter.checkPods 154 155 // 13-Jan-2013 156 // Safari seems to have trouble creating seesion cookie 157 // with proxy server - create session here as a workaround 158 dummy := req.session 159 160 // proxy request 161 c := WebClient() 162 c.followRedirects = false 163 c.reqUri = `http://localhost:${port}${req.uri.relToAuth}` 164 c.reqMethod = req.method 165 req.headers.each |v,k| 166 { 167// TODO FIXIT: 4-Jun-2014 168// WebClient does not support sending multiple Set-Cookie headers 169// so can break applications behind the proxy. This is likley the 170// same problem as the above Safari hack. 171 if (k == "Host") return 172 c.reqHeaders[k] = v 173 } 174 c.writeReq 175 176 is100Continue := c.reqHeaders["Expect"] == "100-continue" 177 178 if (req.method == "POST" && ! is100Continue) 179 c.reqOut.writeBuf(req.in.readAllBuf).flush 180 181 // proxy response 182 c.readRes 183 184 if (is100Continue && c.resCode == 100) 185 { 186 c.reqOut.writeBuf(req.in.readAllBuf).flush 187 c.readRes // final response after the 100continue 188 } 189 190 res.statusCode = c.resCode 191 c.resHeaders.each |v,k| 192 { 193 // we don't re-gzip responses 194 if (k == "Content-Encoding" && v == "gzip") return 195 res.headers[k] = v 196 } 197 198 if (c.resHeaders["Content-Type"] != null || 199 c.resHeaders["Content-Length"] != null) 200 res.out.writeBuf(c.resIn.readAllBuf).flush 201 c.close 202 } 203}