Commit 61efd58cdb2348b9133fd497bc5f0133bf4f3cb1
1 parent
04651a369b
Exists in
master
and in
1 other branch
Put hotload enabling in separate function
Showing
1 changed file
with
7 additions
and
4 deletions
Show diff stats
json_utility.go
1 | package webutility | 1 | package webutility |
2 | 2 | ||
3 | import ( | 3 | import ( |
4 | "encoding/json" | 4 | "encoding/json" |
5 | "errors" | 5 | "errors" |
6 | "fmt" | 6 | "fmt" |
7 | "io" | 7 | "io" |
8 | "net/http" | 8 | "net/http" |
9 | "os" | 9 | "os" |
10 | "sync" | 10 | "sync" |
11 | "time" | 11 | "time" |
12 | 12 | ||
13 | "git.to-net.rs/marko.tikvic/gologger" | 13 | "git.to-net.rs/marko.tikvic/gologger" |
14 | 14 | ||
15 | "gopkg.in/rana/ora.v4" | 15 | "gopkg.in/rana/ora.v4" |
16 | ) | 16 | ) |
17 | 17 | ||
18 | var ( | 18 | var ( |
19 | mu = &sync.Mutex{} | 19 | mu = &sync.Mutex{} |
20 | metadata = make(map[string]Payload) | 20 | metadata = make(map[string]Payload) |
21 | updateQue = make(map[string][]byte) | 21 | updateQue = make(map[string][]byte) |
22 | 22 | ||
23 | metadataDB *ora.Ses | 23 | metadataDB *ora.Ses |
24 | activeProject string | 24 | activeProject string |
25 | 25 | ||
26 | inited bool | 26 | inited bool |
27 | ) | 27 | ) |
28 | 28 | ||
29 | var logger *gologger.Logger | 29 | var logger *gologger.Logger |
30 | 30 | ||
31 | func init() { | 31 | func init() { |
32 | var err error | 32 | var err error |
33 | logger, err = gologger.New("metadata", gologger.MaxLogSize100KB) | 33 | logger, err = gologger.New("metadata", gologger.MaxLogSize100KB) |
34 | if err != nil { | 34 | if err != nil { |
35 | fmt.Printf("webutility: %s\n", err.Error()) | 35 | fmt.Printf("webutility: %s\n", err.Error()) |
36 | } | 36 | } |
37 | } | 37 | } |
38 | 38 | ||
39 | type LangMap map[string]map[string]string | 39 | type LangMap map[string]map[string]string |
40 | 40 | ||
41 | type Field struct { | 41 | type Field struct { |
42 | Parameter string `json:"param"` | 42 | Parameter string `json:"param"` |
43 | Type string `json:"type"` | 43 | Type string `json:"type"` |
44 | Visible bool `json:"visible"` | 44 | Visible bool `json:"visible"` |
45 | Editable bool `json:"editable"` | 45 | Editable bool `json:"editable"` |
46 | } | 46 | } |
47 | 47 | ||
48 | type CorrelationField struct { | 48 | type CorrelationField struct { |
49 | Result string `json:"result"` | 49 | Result string `json:"result"` |
50 | Elements []string `json:"elements"` | 50 | Elements []string `json:"elements"` |
51 | Type string `json:"type"` | 51 | Type string `json:"type"` |
52 | } | 52 | } |
53 | 53 | ||
54 | type Translation struct { | 54 | type Translation struct { |
55 | Language string `json:"language"` | 55 | Language string `json:"language"` |
56 | FieldsLabels map[string]string `json:"fieldsLabels"` | 56 | FieldsLabels map[string]string `json:"fieldsLabels"` |
57 | } | 57 | } |
58 | 58 | ||
59 | type Payload struct { | 59 | type Payload struct { |
60 | Method string `json:"method"` | 60 | Method string `json:"method"` |
61 | Params map[string]string `json:"params"` | 61 | Params map[string]string `json:"params"` |
62 | Lang []Translation `json:"lang"` | 62 | Lang []Translation `json:"lang"` |
63 | Fields []Field `json:"fields"` | 63 | Fields []Field `json:"fields"` |
64 | Correlations []CorrelationField `json:"correlationFields"` | 64 | Correlations []CorrelationField `json:"correlationFields"` |
65 | IdField string `json:"idField"` | 65 | IdField string `json:"idField"` |
66 | 66 | ||
67 | // Data holds JSON payload. It can't be used for itteration. | 67 | // Data holds JSON payload. It can't be used for itteration. |
68 | Data interface{} `json:"data"` | 68 | Data interface{} `json:"data"` |
69 | } | 69 | } |
70 | 70 | ||
71 | // LoadPayloadsdetaData loads all payloads' information into 'metadata' variable. | 71 | // LoadPayloadsdetaData loads all payloads' information into 'metadata' variable. |
72 | func LoadPayloadsMetadata(db *ora.Ses, project string, hotloading bool, hlPeriod int) error { | 72 | func LoadPayloadsMetadata(db *ora.Ses, project string) error { |
73 | metadataDB = db | 73 | metadataDB = db |
74 | activeProject = project | 74 | activeProject = project |
75 | 75 | ||
76 | mu.Lock() | 76 | mu.Lock() |
77 | defer mu.Unlock() | 77 | defer mu.Unlock() |
78 | err := initMetadata(project) | 78 | err := initMetadata(project) |
79 | if err != nil { | 79 | if err != nil { |
80 | return err | 80 | return err |
81 | } | 81 | } |
82 | if hotloading { | ||
83 | go hotload(hlPeriod) | ||
84 | } | ||
85 | inited = true | 82 | inited = true |
86 | 83 | ||
87 | return nil | 84 | return nil |
88 | } | 85 | } |
89 | 86 | ||
87 | func EnableHotloading(interval int) { | ||
88 | if interval > 0 { | ||
89 | go hotload(interval) | ||
90 | } | ||
91 | } | ||
92 | |||
90 | func GetMetadataForAllEntities() map[string]Payload { | 93 | func GetMetadataForAllEntities() map[string]Payload { |
91 | return metadata | 94 | return metadata |
92 | } | 95 | } |
93 | 96 | ||
94 | func GetMetadataForEntity(t string) (Payload, bool) { | 97 | func GetMetadataForEntity(t string) (Payload, bool) { |
95 | p, ok := metadata[t] | 98 | p, ok := metadata[t] |
96 | return p, ok | 99 | return p, ok |
97 | } | 100 | } |
98 | 101 | ||
99 | func QueEntityModelUpdate(entityType string, v interface{}) { | 102 | func QueEntityModelUpdate(entityType string, v interface{}) { |
100 | updateQue[entityType], _ = json.Marshal(v) | 103 | updateQue[entityType], _ = json.Marshal(v) |
101 | } | 104 | } |
102 | 105 | ||
103 | func UpdateEntityModels(command string) (total, upd, add int, err error) { | 106 | func UpdateEntityModels(command string) (total, upd, add int, err error) { |
104 | if command != "force" && command != "missing" { | 107 | if command != "force" && command != "missing" { |
105 | return total, 0, 0, errors.New("uknown command: " + command) | 108 | return total, 0, 0, errors.New("uknown command: " + command) |
106 | } | 109 | } |
107 | 110 | ||
108 | if !inited { | 111 | if !inited { |
109 | return 0, 0, 0, errors.New("webutil: metadata not initialized but update was tried.") | 112 | return 0, 0, 0, errors.New("webutil: metadata not initialized but update was tried.") |
110 | } | 113 | } |
111 | 114 | ||
112 | total = len(updateQue) | 115 | total = len(updateQue) |
113 | 116 | ||
114 | forUpdate := make([]string, 0) | 117 | forUpdate := make([]string, 0) |
115 | forAdd := make([]string, 0) | 118 | forAdd := make([]string, 0) |
116 | 119 | ||
117 | for k, _ := range updateQue { | 120 | for k, _ := range updateQue { |
118 | if _, exists := metadata[k]; exists { | 121 | if _, exists := metadata[k]; exists { |
119 | if command == "force" { | 122 | if command == "force" { |
120 | forUpdate = append(forUpdate, k) | 123 | forUpdate = append(forUpdate, k) |
121 | } | 124 | } |
122 | } else { | 125 | } else { |
123 | forAdd = append(forAdd, k) | 126 | forAdd = append(forAdd, k) |
124 | } | 127 | } |
125 | } | 128 | } |
126 | 129 | ||
127 | for _, k := range forUpdate { | 130 | for _, k := range forUpdate { |
128 | _, err := metadataDB.PrepAndExe(`update entities set | 131 | _, err := metadataDB.PrepAndExe(`update entities set |
129 | entity_model = :1 | 132 | entity_model = :1 |
130 | where entity_type = :2`, | 133 | where entity_type = :2`, |
131 | string(updateQue[k]), | 134 | string(updateQue[k]), |
132 | k) | 135 | k) |
133 | 136 | ||
134 | if err != nil { | 137 | if err != nil { |
135 | logger.Log("webutility: update metadata: prep and exe: %v\n", err) | 138 | logger.Log("webutility: update metadata: prep and exe: %v\n", err) |
136 | continue | 139 | continue |
137 | } | 140 | } |
138 | upd++ | 141 | upd++ |
139 | logger.Log("webutility: updated %s payload model\n", k) | 142 | logger.Log("webutility: updated %s payload model\n", k) |
140 | } | 143 | } |
141 | 144 | ||
142 | blankPayload, _ := json.Marshal(Payload{}) | 145 | blankPayload, _ := json.Marshal(Payload{}) |
143 | for _, k := range forAdd { | 146 | for _, k := range forAdd { |
144 | _, err := metadataDB.PrepAndExe(`insert into entities | 147 | _, err := metadataDB.PrepAndExe(`insert into entities |
145 | (projekat, metadata, entity_type, entity_model) | 148 | (projekat, metadata, entity_type, entity_model) |
146 | values(:1, :2, :3, :4)`, | 149 | values(:1, :2, :3, :4)`, |
147 | activeProject, | 150 | activeProject, |
148 | string(blankPayload), | 151 | string(blankPayload), |
149 | k, | 152 | k, |
150 | string(updateQue[k])) | 153 | string(updateQue[k])) |
151 | 154 | ||
152 | if err != nil { | 155 | if err != nil { |
153 | logger.Log("webutility: add metadata: prep and exe: %v\n", err) | 156 | logger.Log("webutility: add metadata: prep and exe: %v\n", err) |
154 | continue | 157 | continue |
155 | } | 158 | } |
156 | metadata[k] = Payload{} | 159 | metadata[k] = Payload{} |
157 | add++ | 160 | add++ |
158 | logger.Log("webutility: added %s to the payload models\n", k) | 161 | logger.Log("webutility: added %s to the payload models\n", k) |
159 | 162 | ||
160 | } | 163 | } |
161 | 164 | ||
162 | return total, upd, add, nil | 165 | return total, upd, add, nil |
163 | } | 166 | } |
164 | 167 | ||
165 | func ModifyMetadataForEntity(entityType string, p *Payload) error { | 168 | func ModifyMetadataForEntity(entityType string, p *Payload) error { |
166 | md, err := json.Marshal(*p) | 169 | md, err := json.Marshal(*p) |
167 | if err != nil { | 170 | if err != nil { |
168 | return err | 171 | return err |
169 | } | 172 | } |
170 | 173 | ||
171 | mu.Lock() | 174 | mu.Lock() |
172 | defer mu.Unlock() | 175 | defer mu.Unlock() |
173 | _, err = metadataDB.PrepAndExe(`update entities set | 176 | _, err = metadataDB.PrepAndExe(`update entities set |
174 | metadata = :1 | 177 | metadata = :1 |
175 | where projekat = :2 | 178 | where projekat = :2 |
176 | and entity_type = :3`, | 179 | and entity_type = :3`, |
177 | string(md), | 180 | string(md), |
178 | activeProject, | 181 | activeProject, |
179 | entityType) | 182 | entityType) |
180 | if err != nil { | 183 | if err != nil { |
181 | return err | 184 | return err |
182 | } | 185 | } |
183 | return nil | 186 | return nil |
184 | } | 187 | } |
185 | 188 | ||
186 | func DeleteEntityModel(entityType string) error { | 189 | func DeleteEntityModel(entityType string) error { |
187 | _, err := metadataDB.PrepAndExe("delete from entities where entity_type = :1", entityType) | 190 | _, err := metadataDB.PrepAndExe("delete from entities where entity_type = :1", entityType) |
188 | if err == nil { | 191 | if err == nil { |
189 | mu.Lock() | 192 | mu.Lock() |
190 | delete(metadata, entityType) | 193 | delete(metadata, entityType) |
191 | mu.Unlock() | 194 | mu.Unlock() |
192 | } | 195 | } |
193 | return err | 196 | return err |
194 | } | 197 | } |
195 | 198 | ||
196 | // NewPayload returs a payload sceleton for entity described with etype. | 199 | // NewPayload returs a payload sceleton for entity described with etype. |
197 | func NewPayload(r *http.Request, etype string) Payload { | 200 | func NewPayload(r *http.Request, etype string) Payload { |
198 | pload := metadata[etype] | 201 | pload := metadata[etype] |
199 | pload.Method = r.Method + " " + r.RequestURI | 202 | pload.Method = r.Method + " " + r.RequestURI |
200 | return pload | 203 | return pload |
201 | } | 204 | } |
202 | 205 | ||
203 | // DecodeJSON decodes JSON data from r to v. | 206 | // DecodeJSON decodes JSON data from r to v. |
204 | // Returns an error if it fails. | 207 | // Returns an error if it fails. |
205 | func DecodeJSON(r io.Reader, v interface{}) error { | 208 | func DecodeJSON(r io.Reader, v interface{}) error { |
206 | return json.NewDecoder(r).Decode(v) | 209 | return json.NewDecoder(r).Decode(v) |
207 | } | 210 | } |
208 | 211 | ||
209 | func initMetadata(project string) error { | 212 | func initMetadata(project string) error { |
210 | metadataDB.SetCfg(metadataDB.Cfg().SetClob(ora.S)) | 213 | metadataDB.SetCfg(metadataDB.Cfg().SetClob(ora.S)) |
211 | stmt, err := metadataDB.Prep(`select | 214 | stmt, err := metadataDB.Prep(`select |
212 | entity_type, | 215 | entity_type, |
213 | metadata | 216 | metadata |
214 | from entities | 217 | from entities |
215 | where projekat = `+fmt.Sprintf("'%s'", project), | 218 | where projekat = `+fmt.Sprintf("'%s'", project), |
216 | ora.S, | 219 | ora.S, |
217 | ora.S) | 220 | ora.S) |
218 | 221 | ||
219 | defer stmt.Close() | 222 | defer stmt.Close() |
220 | if err != nil { | 223 | if err != nil { |
221 | return err | 224 | return err |
222 | } | 225 | } |
223 | 226 | ||
224 | rset, err := stmt.Qry() | 227 | rset, err := stmt.Qry() |
225 | if err != nil { | 228 | if err != nil { |
226 | return err | 229 | return err |
227 | } | 230 | } |
228 | 231 | ||
229 | count := 0 | 232 | count := 0 |
230 | success := 0 | 233 | success := 0 |
231 | if len(metadata) > 0 { | 234 | if len(metadata) > 0 { |
232 | metadata = nil | 235 | metadata = nil |
233 | } | 236 | } |
234 | metadata = make(map[string]Payload) | 237 | metadata = make(map[string]Payload) |
235 | for rset.Next() { | 238 | for rset.Next() { |
236 | name := rset.Row[0].(string) | 239 | name := rset.Row[0].(string) |
237 | load := []byte(rset.Row[1].(string)) | 240 | load := []byte(rset.Row[1].(string)) |
238 | 241 | ||
239 | p := Payload{} | 242 | p := Payload{} |
240 | err := json.Unmarshal(load, &p) | 243 | err := json.Unmarshal(load, &p) |
241 | if err != nil { | 244 | if err != nil { |
242 | logger.Log("couldn't init: '%s' metadata: %s\n%s\n", name, err.Error(), rset.Row[1].(string)) | 245 | logger.Log("couldn't init: '%s' metadata: %s\n%s\n", name, err.Error(), rset.Row[1].(string)) |
243 | } else { | 246 | } else { |
244 | success++ | 247 | success++ |
245 | metadata[name] = p | 248 | metadata[name] = p |
246 | } | 249 | } |
247 | count++ | 250 | count++ |
248 | } | 251 | } |
249 | logger.Log("webutility: successfully loaded %d/%d (%.1f%%) entities\n", success, count, float32(success)/float32(count)*100.0) | 252 | logger.Log("webutility: successfully loaded %d/%d (%.1f%%) entities\n", success, count, float32(success)/float32(count)*100.0) |
250 | 253 | ||
251 | return nil | 254 | return nil |
252 | } | 255 | } |
253 | 256 | ||
254 | func hotload(n int) { | 257 | func hotload(n int) { |
255 | entityScan := make(map[string]int64) | 258 | entityScan := make(map[string]int64) |
256 | firstCheck := true | 259 | firstCheck := true |
257 | for { | 260 | for { |
258 | time.Sleep(time.Duration(n) * time.Second) | 261 | time.Sleep(time.Duration(n) * time.Second) |
259 | stmt, err := metadataDB.Prep(`select | 262 | stmt, err := metadataDB.Prep(`select |
260 | ora_rowscn, | 263 | ora_rowscn, |
261 | entity_type | 264 | entity_type |
262 | from entities where projekat = `+fmt.Sprintf("'%s'", activeProject), | 265 | from entities where projekat = `+fmt.Sprintf("'%s'", activeProject), |
263 | ora.I64, | 266 | ora.I64, |
264 | ora.S) | 267 | ora.S) |
265 | if err != nil { | 268 | if err != nil { |
266 | logger.Log("hotload failed: %v\n", err) | 269 | logger.Log("hotload failed: %v\n", err) |
267 | time.Sleep(time.Duration(n) * time.Second) | 270 | time.Sleep(time.Duration(n) * time.Second) |
268 | continue | 271 | continue |
269 | } | 272 | } |
270 | 273 | ||
271 | rset, err := stmt.Qry() | 274 | rset, err := stmt.Qry() |
272 | if err != nil { | 275 | if err != nil { |
273 | fmt.Fprintf(os.Stderr, "hotload failed: %v\n", err) | 276 | fmt.Fprintf(os.Stderr, "hotload failed: %v\n", err) |
274 | time.Sleep(time.Duration(n) * time.Second) | 277 | time.Sleep(time.Duration(n) * time.Second) |
275 | continue | 278 | continue |
276 | } | 279 | } |
277 | 280 | ||
278 | var toRefresh []string | 281 | var toRefresh []string |
279 | for rset.Next() { | 282 | for rset.Next() { |
280 | scanID := rset.Row[0].(int64) | 283 | scanID := rset.Row[0].(int64) |
281 | entity := rset.Row[1].(string) | 284 | entity := rset.Row[1].(string) |
282 | oldID, ok := entityScan[entity] | 285 | oldID, ok := entityScan[entity] |
283 | if !ok || oldID != scanID { | 286 | if !ok || oldID != scanID { |
284 | entityScan[entity] = scanID | 287 | entityScan[entity] = scanID |
285 | toRefresh = append(toRefresh, entity) | 288 | toRefresh = append(toRefresh, entity) |
286 | } | 289 | } |
287 | } | 290 | } |
288 | stmt.Close() | 291 | stmt.Close() |
289 | 292 | ||
290 | if rset.Err() != nil { | 293 | if rset.Err() != nil { |
291 | logger.Log("hotload rset error: %v\n", rset.Err()) | 294 | logger.Log("hotload rset error: %v\n", rset.Err()) |
292 | time.Sleep(time.Duration(n) * time.Second) | 295 | time.Sleep(time.Duration(n) * time.Second) |
293 | continue | 296 | continue |
294 | } | 297 | } |
295 | 298 | ||
296 | if len(toRefresh) > 0 && !firstCheck { | 299 | if len(toRefresh) > 0 && !firstCheck { |
297 | mu.Lock() | 300 | mu.Lock() |
298 | refreshMetadata(toRefresh) | 301 | refreshMetadata(toRefresh) |
299 | mu.Unlock() | 302 | mu.Unlock() |
300 | } | 303 | } |
301 | if firstCheck { | 304 | if firstCheck { |
302 | firstCheck = false | 305 | firstCheck = false |
303 | } | 306 | } |
304 | } | 307 | } |
305 | } | 308 | } |
306 | 309 | ||
307 | func refreshMetadata(entities []string) { | 310 | func refreshMetadata(entities []string) { |
308 | for _, e := range entities { | 311 | for _, e := range entities { |
309 | fmt.Printf("refreshing %s\n", e) | 312 | fmt.Printf("refreshing %s\n", e) |
310 | stmt, err := metadataDB.Prep(`select | 313 | stmt, err := metadataDB.Prep(`select |
311 | metadata | 314 | metadata |
312 | from entities | 315 | from entities |
313 | where projekat = `+fmt.Sprintf("'%s'", activeProject)+ | 316 | where projekat = `+fmt.Sprintf("'%s'", activeProject)+ |
314 | ` and entity_type = `+fmt.Sprintf("'%s'", e), | 317 | ` and entity_type = `+fmt.Sprintf("'%s'", e), |
315 | ora.S) | 318 | ora.S) |
316 | 319 | ||
317 | if err != nil { | 320 | if err != nil { |
318 | logger.Log("webutility: refresh: prep: %v\n", err) | 321 | logger.Log("webutility: refresh: prep: %v\n", err) |
319 | stmt.Close() | 322 | stmt.Close() |
320 | continue | 323 | continue |
321 | } | 324 | } |
322 | 325 | ||
323 | rset, err := stmt.Qry() | 326 | rset, err := stmt.Qry() |
324 | if err != nil { | 327 | if err != nil { |
325 | fmt.Printf("webutility: refresh: query: %v\n", err) | 328 | fmt.Printf("webutility: refresh: query: %v\n", err) |
326 | stmt.Close() | 329 | stmt.Close() |
327 | continue | 330 | continue |
328 | } | 331 | } |
329 | 332 | ||
330 | for rset.Next() { | 333 | for rset.Next() { |
331 | load := []byte(rset.Row[0].(string)) | 334 | load := []byte(rset.Row[0].(string)) |
332 | p := Payload{} | 335 | p := Payload{} |
333 | err := json.Unmarshal(load, &p) | 336 | err := json.Unmarshal(load, &p) |
334 | if err != nil { | 337 | if err != nil { |
335 | logger.Log("couldn't refresh: '%s' metadata: %s\n%s\n", e, err.Error(), rset.Row[0].(string)) | 338 | logger.Log("couldn't refresh: '%s' metadata: %s\n%s\n", e, err.Error(), rset.Row[0].(string)) |
336 | } else { | 339 | } else { |
337 | metadata[e] = p | 340 | metadata[e] = p |
338 | } | 341 | } |
339 | } | 342 | } |
340 | stmt.Close() | 343 | stmt.Close() |