Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

1229
LINES

< > BotCompany Repo | #1034278 // "Concepts" (persistent Java objects, backup)

JavaX fragment (include)

1  
// A concept should be an object, not just a string.
2  
3  
// Functions that should always be there for child processes:
4  
please include function dbLock.
5  
6  
static int concepts_internStringsLongerThan = 10;
7  
8  
static new ThreadLocal<Bool> concepts_unlisted;
9  
10  
// BREAKING CHANGE 2021/6/7 set to true
11  
sbool concepts_unlistedByDefault = true; // true = we can create instances of concepts with "new" without registering them automatically
12  
13  
interface IConceptIndex {
14  
  void update(Concept c); // also for adding
15  
  void remove(Concept c);
16  
}
17  
18  
interface IFieldIndex<A extends Concept, Val> {
19  
  Cl<A> getAll(Val val);
20  
  L<Val> allValues(); // returns a cloned list
21  
  MultiSet<Val> allValues_multiSet();
22  
  ItIt<A> objectIterator();
23  
}
24  
25  
// Approach to persisting the Concepts object itself (in normal
26  
// DB operation, this is not done): For simplification, speed and
27  
// compactness, we make almost all the fields transient and store only // the concepts and the idCounter. To unstructure the Concepts object,
28  
// use unstructureConcepts() or postUnstructureConcepts(), then
29  
// re-set up any indices, listeners etc.
30  
31  
// base class indicating nothing
32  
sclass ConceptsChange {}
33  
34  
// change of a single concept
35  
srecord ConceptCreate(Concept c) extends ConceptsChange {}
36  
37  
// change of a single concept
38  
srecord ConceptChange(Concept c) extends ConceptsChange {}
39  
40  
// removal of a single concept
41  
// c.id is going to become 0 at some point, so we pass the
42  
// id separately
43  
srecord ConceptDelete(long id, Concept c) extends ConceptsChange {}
44  
45  
// unknown change anywhere in concepts; consider it all dirty
46  
// (this one should not be used except for batch jobs)
47  
srecord FullChange extends ConceptsChange {}
48  
49  
sclass Concepts implements AutoCloseable {
50  
  Map<Long, Concept> concepts = synchroTreeMap();
51  
  long idCounter;
52  
  
53  
  transient HashMap<Class, O> perClassData;
54  
  transient Map miscMap; // don't use directly, call miscMap... methods to access
55  
  
56  
  // set to "-" for non-persistent (possibly not implemented)
57  
  // also, can include a case ID ("#123/1")
58  
  // TODO: have an actual directory instead
59  
  transient S programID;
60  
  
61  
  transient Concepts parent; // new mechanism
62  
  transient volatile long changes, changesWritten, lastChange;
63  
  transient volatile java.util.Timer autoSaver;
64  
  transient volatile bool dontSave;
65  
  transient volatile bool savingConcepts, noXFullGrab;
66  
  transient bool vmBusSend = true;
67  
  transient bool initialSave = false; // set to false to avoid initial useless saving
68  
  transient int autoSaveInterval = -1000; // 1 second + wait logic
69  
  transient bool useGZIP = true, quietSave;
70  
  transient ReentrantLock lock = new ReentrantLock(true);
71  
  transient ReentrantLock saverLock = new ReentrantLock(true);
72  
  transient long lastSaveTook = -1, lastSaveWas, loadTook, uncompressedSize;
73  
  transient float maxAutoSavePercentage = 10;
74  
  transient L<IConceptIndex> conceptIndices;
75  
  transient Map<Class<? extends Concept>, Map<S, IFieldIndex>> fieldIndices;
76  
  transient Map<Class<? extends Concept>, Map<S, IFieldIndex>> ciFieldIndices;
77  
  //transient L saveActions = synchroList();
78  
  transient L<Runnable> preSave;
79  
  transient O classFinder = _defaultClassFinder();
80  
  transient L onAllChanged = synchroList(); // list of runnables
81  
  transient new Set<IVF1> onChange;
82  
  transient O saveWrapper; // VF1<Runnable>, to profile saving
83  
  transient bool modifyOnCreate; // set _modified == created initially
84  
  transient bool modifyOnBackRef; // set modified if back refs change
85  
  transient bool useFileLock = true; // instead of locking by bot
86  
  // OLD - not done anymore. transient bool collectForwardRefs
87  
  transient FileBasedLock fileLock;
88  
  transient bool storeBaseClassesInStructure; // helps with schema evolution when concept subclasses disappear
89  
  transient bool useBackRefsForSearches; // assume backRefs are sane in order to speed up searches
90  
  transient bool defunct;
91  
  transient int newBackupEveryXMinutes = 60;
92  
  
93  
  // add more fields for Concepts here
94  
  
95  
  *() {}
96  
  *(S *programID) {}
97  
  
98  
  synchronized long internalID() {
99  
    do {
100  
      ++idCounter;
101  
    } while (hasConcept(idCounter));
102  
    ret idCounter;
103  
  }
104  
  
105  
  synchronized HashMap<Class, O> perClassData() {
106  
    if (perClassData == null) perClassData = new HashMap;
107  
    ret perClassData;
108  
  }
109  
  
110  
  void initProgramID() {
111  
    if (programID == null)
112  
      programID = getDBProgramID();
113  
  }
114  
  
115  
  // Now tries to load from bot first, then go to disk.
116  
  Concepts load() {
117  
    ret load(false);
118  
  }
119  
  
120  
  Concepts safeLoad() {
121  
    ret load(true);
122  
  }
123  
  
124  
  Concepts load(bool allDynamic) {
125  
    initProgramID();
126  
    
127  
    // try custom grabber
128  
    O dbGrabber = miscMapGet("dbGrabber");
129  
    if (dbGrabber != null && !isFalse(callF(dbGrabber)))
130  
      this;
131  
132  
    try {
133  
      if (tryToGrab(allDynamic)) ret this;
134  
    } catch e {
135  
      if (!exceptionMessageContains(e, "no xfullgrab"))
136  
        printShortException(e);
137  
      print("xfullgrab failed - loading DB of " + programID + " from disk");
138  
    }
139  
    ret loadFromDisk(allDynamic);
140  
  }
141  
  
142  
  Concepts loadFromDisk() { ret loadFromDisk(false); }
143  
  
144  
  Concepts loadFromDisk(bool allDynamic) {
145  
    if (nempty(concepts)) clearConcepts();
146  
    //DynamicObject_loading.set(true); // now done in unstructure()
147  
    //try {
148  
      // minimal crash recovery
149  
      restoreLatestBackupIfConceptsFileEmpty(programID, doIt := true);
150  
151  
      long time = now();
152  
      Map<Long, Concept> _concepts = concepts; // empty map
153  
      readLocally2_allDynamic.set(allDynamic);
154  
      temp tempSetTL(readLocally2_classFinder, classFinder);
155  
      readLocally2(this, programID, "concepts");
156  
      Map<Long, Concept> __concepts = concepts;
157  
      concepts = _concepts;
158  
      concepts.putAll(__concepts);
159  
      int l = readLocally_stringLength;
160  
      int tokrefs = unstructure_tokrefs;
161  
      assignConceptsToUs();
162  
      loadTook = now()-time;
163  
      done("Loaded " + n(l(concepts), "concepts"), time);
164  
      try {
165  
        if (fileSize(getProgramFile(programID, "idCounter.structure")) != 0)
166  
          readLocally2(this, programID, "idCounter");
167  
        else
168  
          calcIdCounter();
169  
      } catch print e {
170  
        calcIdCounter();
171  
      }
172  
    /*} finally {
173  
      DynamicObject_loading.set(null);
174  
    }*/
175  
    ret this;
176  
  }
177  
  
178  
  Concepts loadConcepts() { ret load(); }
179  
  
180  
  bool tryToGrab(bool allDynamic) {
181  
    if (sameSnippetID(programID, getDBProgramID())) false;
182  
    temp RemoteDB db = connectToDBOpt(programID);
183  
    if (db != null) {
184  
      loadGrab(db.fullgrab(), allDynamic);
185  
      true;
186  
    }
187  
    false;
188  
  }
189  
  
190  
  Concepts load(S grab) {
191  
    ret loadGrab(grab, false);
192  
  }
193  
  
194  
  Concepts safeLoad(S grab) {
195  
    ret loadGrab(grab, true);
196  
  }
197  
  
198  
  Concepts loadGrab(S grab, bool allDynamic) {
199  
    clearConcepts();
200  
    DynamicObject_loading.set(true);
201  
    try {
202  
      Map<Long, Concept> map = (Map) unstructure(grab, allDynamic, classFinder);
203  
      concepts.putAll(map);
204  
      assignConceptsToUs();
205  
      for (long l : map.keySet())
206  
        idCounter = max(idCounter, l);
207  
    } finally {
208  
      DynamicObject_loading.set(null);
209  
    }
210  
    //XXX allChanged(); // Nobody is listening at this point anyway
211  
    ret this;
212  
  }
213  
  
214  
  void assignConceptsToUs() {
215  
    // fix unstructure bugs
216  
    
217  
    for (Pair<Long, O> p: mapToPairs((Map<Long, O>) (Map) concepts))
218  
      if (!(p.b instanceof Concept)) {
219  
       print("DROPPING non-existant concept " + p.a + ": " + dynShortName(p.b));
220  
       concepts.remove(p.a);
221  
    }
222  
    
223  
    for (Concept c : values(concepts)) c._concepts = this;
224  
    for (Concept c : values(concepts))
225  
      c._doneLoading2(); // doneLoading2 is called on all concepts after all concepts are loaded
226  
  }
227  
228  
  S progID() {
229  
    ret programID == null ? getDBProgramID() : programID;
230  
  }
231  
  
232  
  Concept getConcept(S id) {
233  
    ret empty(id) ? null : getConcept(parseLong(id));
234  
  }
235  
  
236  
  Concept getConcept(long id) {
237  
    ret (Concept) concepts.get((long) id);
238  
  }
239  
  
240  
  Concept getConcept(RC ref) {
241  
    ret ref == null ? null : getConcept(ref.longID());
242  
  }
243  
  
244  
  bool hasConcept(long id) {
245  
    ret concepts.containsKey((long) id);
246  
  }
247  
  
248  
  void deleteConcept(long id) {
249  
    Concept c = getConcept(id);
250  
    if (c == null)
251  
      print("Concept " + id + " not found");
252  
    else
253  
      c.delete();
254  
  }
255  
  
256  
  void calcIdCounter() {
257  
    long id_ = 0;
258  
    for (long id : keys(concepts))
259  
      id_ = max(id_, id);
260  
    idCounter = id_+1;
261  
    saveLocally2(this, programID, "idCounter");
262  
  }
263  
  
264  
  File conceptsFile() {
265  
    ret getProgramFile(programID, useGZIP ? "concepts.structure.gz" : "concepts.structure");
266  
  }
267  
  
268  
  // used for locking when useFileLock is activated
269  
  File lockFile() {
270  
    ret getProgramFile(programID, "concepts.lock");
271  
  }
272  
  
273  
  FileBasedLock fileLock() {
274  
    if (fileLock == null)
275  
      fileLock = new FileBasedLock(lockFile());
276  
    ret fileLock;
277  
  }
278  
  
279  
  void saveConceptsIfDirty() { saveConcepts(); }
280  
  void save() { saveConcepts(); }
281  
282  
  void saveConcepts() {
283  
    vmBus_send saveConceptsCalled(Concepts.this);
284  
    if (dontSave) ret;
285  
    initProgramID();
286  
    saverLock.lock();
287  
    savingConcepts = true;
288  
    long start = now(), time;
289  
    try {
290  
      S s = null;
291  
      //synchronized(main.class) {
292  
      long _changes = changes;
293  
      if (_changes == changesWritten) ret;
294  
      
295  
      File f = conceptsFile();
296  
      
297  
      lock.lock();
298  
      long fullTime = now();
299  
      try {
300  
        saveLocally2(this, programID, "idCounter");
301  
        
302  
        vmBus_send idCounterWritten(Concepts.this, programID, "idCounter");
303  
        
304  
        if (useGZIP) {
305  
          vmBus_send callingSaveWrapper(Concepts.this, saveWrapper);
306  
          callRunnableWithWrapper(saveWrapper, r {
307  
            vmBus_send callingPreSave(Concepts.this, preSave);
308  
            callFAll(preSave);
309  
            vmBus_send writingFile(Concepts.this, f);
310  
            uncompressedSize = saveGZStructureToFile(f, cloneMap(concepts), makeStructureData());
311  
            vmBus_send gzFileSaved(Concepts.this, f, uncompressedSize);
312  
          });
313  
          getProgramFile(programID, "concepts.structure").delete();
314  
        } else
315  
          s = fullStructure();
316  
      } finally {
317  
        lock.unlock();
318  
      }
319  
      
320  
      /*while (nempty(saveActions))
321  
        pcallF(popFirst(saveActions));*/
322  
323  
      changesWritten = _changes; // only update when structure didn't fail
324  
      
325  
      if (!useGZIP) {
326  
        time = now()-start;
327  
        if (!quietSave)
328  
          print("Saving " + toM(l(s)) + "M chars (" /*+ changesWritten + ", "*/ + time + " ms)");
329  
        start = now();
330  
        saveTextFile(f, javaTokWordWrap(s));
331  
        getProgramFile(programID, "concepts.structure.gz").delete();
332  
      }
333  
      
334  
      File backupFile = getProgramFile(programID, "backups/concepts.structure" + (useGZIP ? ".gz" : "") + ".backup" + ymd() + "-" + formatInt(hours(), 2)
335  
        + (newBackupEveryXMinutes >= 60 ? "" : formatInt(roundDownTo_rev(minutes(), newBackupEveryXMinutes), 2)));
336  
      copyFile(f, backupFile);
337  
      
338  
      time = now()-start;
339  
      if (!quietSave)
340  
        print(programID + ": Saved " + toK(f.length()) + " K, " + n(concepts, "concepts") + " (" + time + " ms)");
341  
      lastSaveWas = fullTime;
342  
      lastSaveTook = now()-fullTime;
343  
    } finally {
344  
      savingConcepts = false;
345  
      saverLock.unlock();
346  
    }
347  
  }
348  
  
349  
  void _autoSaveConcepts() {
350  
    if (autoSaveInterval < 0 && maxAutoSavePercentage != 0) {
351  
      long pivotTime = Math.round(lastSaveWas+lastSaveTook*100.0/maxAutoSavePercentage);
352  
      if (now() < pivotTime) {
353  
        //print("Skipping auto-save (last save took " + lastSaveTook + ")");
354  
        ret;
355  
      }
356  
    }
357  
    try {
358  
      saveConcepts();
359  
    } catch e {
360  
      print("Concept save failed, will try again");
361  
      printStackTrace(e);
362  
    }
363  
  }
364  
  
365  
  S fullStructure() {
366  
    ret structure(cloneMap(concepts), makeStructureData());
367  
  }
368  
  
369  
  swappable structure_Data makeStructureData() {
370  
    ret finishStructureData(new structure_Data);
371  
  }
372  
  
373  
  structure_Data finishStructureData(structure_Data data) {
374  
    if (storeBaseClassesInStructure)
375  
      data.storeBaseClasses = true;
376  
    ret data;
377  
  }
378  
  
379  
  void clearConcepts() {
380  
    for (Concept c : allConcepts()) c.delete();
381  
    //concepts.clear();
382  
    //allChanged();
383  
  }
384  
  
385  
  void fireLegacyChangeEvent() {
386  
    synchronized(this) { ++changes; lastChange = sysNow(); }
387  
    if (vmBusSend) vmBus_send conceptsChanged(this);
388  
    pcallFAll(onAllChanged);
389  
  }
390  
  
391  
  // auto-save every second if dirty
392  
  synchronized void autoSaveConcepts() {
393  
    if (autoSaver == null) {
394  
      if (isTransient()) fail("Can't persist transient database");
395  
      autoSaver = doEvery_daemon("Concepts Saver for " + programID,
396  
        abs(autoSaveInterval), r { _autoSaveConcepts() });
397  
      // print("Installed auto-saver (" + autoSaveInterval + " ms, " + progID() + ")");
398  
    }
399  
  }
400  
  
401  
  public void close { cleanMeUp(); }
402  
  
403  
  void cleanMeUp() {
404  
    pcall {
405  
      set defunct;
406  
      bool shouldSave = autoSaver != null;
407  
      if (autoSaver != null) {
408  
        autoSaver.cancel();
409  
        autoSaver = null;
410  
      }
411  
      while (savingConcepts) sleepInCleanUp(10);
412  
      if (shouldSave)
413  
        saveConceptsIfDirty();
414  
    }
415  
    dispose fileLock;
416  
  }
417  
  
418  
  Map<Long, S> getIDsAndNames() {
419  
    new Map<Long, S> map;
420  
    Map<Long, Concept> cloned = cloneMap(concepts);
421  
    for (long id : keys(cloned)) 
422  
      map.put(id, cloned.get(id).className);
423  
    ret map;
424  
  }
425  
  
426  
  void deleteConcepts(L l) {
427  
    ping();
428  
    if (l != null) for (O o : cloneList(l))
429  
      if (o instanceof Long) {
430  
        Concept c = concepts.get(o);
431  
        if (c != null) c.delete();
432  
      } else if (o cast Concept)
433  
        o.delete();
434  
      else
435  
        warn("Can't delete " + getClassName(o));
436  
  }
437  
  
438  
  <A extends Concept> A conceptOfType(Class<A> type) {
439  
    IConceptCounter counter = conceptCounterForClass(type);
440  
    if (counter != null) ret (A) first(counter.allConcepts());
441  
    ret firstOfType(allConcepts(), type);
442  
  }
443  
  
444  
  <A extends Concept> L<A> conceptsOfType(Class<A> type) {
445  
    L<A> l = conceptsOfType_noParent(type);
446  
    if (parent == null) ret l;
447  
    ret concatLists_conservative(l, parent.conceptsOfType(type));
448  
  }
449  
  
450  
  <A extends Concept> L<A> conceptsOfType_noParent(Class<A> type) {
451  
    ping();
452  
    IConceptCounter counter = conceptCounterForClass(type);
453  
    if (counter != null) ret (L<A>) cloneList(counter.allConcepts());
454  
    ret filterByType(allConcepts(), type);
455  
  }
456  
  
457  
  <A extends Concept> L<A> listConcepts(Class<A> type) {
458  
    ret conceptsOfType(type);
459  
  }
460  
  
461  
  <A extends Concept> L<A> list(Class<A> type) {
462  
    ret conceptsOfType(type);
463  
  }
464  
  
465  
  <A extends Concept> L<A> list_noParent(Class<A> type) {
466  
    ret conceptsOfType_noParent(type);
467  
  }
468  
  
469  
  // TODO: would be better to make this Cl (indices may return sets)
470  
  L<Concept> list(S type) {
471  
    ret conceptsOfType(type);
472  
  }
473  
  
474  
  L<Concept> conceptsOfType(S type) {
475  
    ret filterByDynamicType(allConcepts(), "main$" + type);
476  
  }
477  
  
478  
  bool hasConceptOfType(Class<? extends Concept> type) {
479  
    ret hasType(allConcepts(), type);
480  
  }
481  
  
482  
  void persistConcepts() {
483  
    loadConcepts();
484  
    autoSaveConcepts();
485  
  }
486  
  
487  
  // We love synonyms
488  
  void conceptPersistence() { persistConcepts(); }
489  
  
490  
  Concepts persist() { persistConcepts(); ret this; }
491  
  void persist(Int interval) {
492  
    if (interval != null) autoSaveInterval = interval;
493  
    persist();
494  
  }
495  
    
496  
  // Runs r if there is no concept of that type
497  
  <A extends Concept> A ensureHas(Class<A> c, Runnable r) {
498  
    A a = conceptOfType(c);
499  
    if (a == null) {
500  
      r.run();
501  
      a = conceptOfType(c);
502  
      if (a == null)
503  
        fail("Concept not made by " + r + ": " + shortClassName(c));
504  
    }
505  
    ret a;
506  
  }
507  
  
508  
  // Ensures that every concept of type c1 is ref'd by a concept of
509  
  // type c2.
510  
  // Type of func: voidfunc(concept)
511  
  void ensureHas(Class<? extends Concept> c1, Class<? extends Concept> c2, O func) {
512  
    for (Concept a : conceptsOfType(c1)) {
513  
      Concept b = findBackRef(a, c2);
514  
      if (b == null) {
515  
        callF(func, a);
516  
        b = findBackRef(a, c2);
517  
        if (b == null)
518  
          fail("Concept not made by " + func + ": " + shortClassName(c2));
519  
      }
520  
    }
521  
  }
522  
  
523  
  // Type of func: voidfunc(concept)
524  
  void forEvery(Class<? extends Concept> type, O func) {
525  
    for (Concept c : conceptsOfType(type))
526  
      callF(func, c);
527  
  }
528  
  
529  
  int deleteAll(Class<? extends Concept> type) {
530  
    L<Concept> l = (L) conceptsOfType(type);
531  
    for (Concept c : l) c.delete();
532  
    ret l(l);
533  
  }
534  
  
535  
  // always returns a new list (callers depend on this)
536  
  Collection<Concept> allConcepts() {
537  
    synchronized(concepts) {
538  
      ret new L(values(concepts));
539  
    }
540  
  }
541  
  
542  
  IConceptCounter conceptCounterForClass(Class<? extends Concept> c) {
543  
    for (IFieldIndex idx : values(mapGet(fieldIndices, c)))
544  
      if (idx cast IConceptCounter) ret idx;
545  
    for (IFieldIndex idx : values(mapGet(ciFieldIndices, c)))
546  
      if (idx cast IConceptCounter) ret idx;
547  
    null;
548  
  }
549  
  
550  
551  
  <A extends Concept> int countConcepts(Class<A> c, O... params) {
552  
    int n = countConcepts_noParent(c, params);
553  
    if (parent == null) ret n;
554  
    ret n+parent.countConcepts(c, params);
555  
  }
556  
  
557  
  <A extends Concept> int countConcepts_noParent(Class<A> c, O... params) {
558  
    ping();
559  
    if (empty(params)) {
560  
      IConceptCounter counter = conceptCounterForClass(c);
561  
      if (counter != null) ret counter.countConcepts();
562  
      ret l(list_noParent(c));
563  
    }
564  
    int n = 0;
565  
    for (A x : list_noParent(c)) if (checkConceptFields(x, params)) ++n;
566  
    ret n;
567  
  }
568  
569  
  int countConcepts(S c, O... params) {
570  
    ping();
571  
    if (empty(params)) ret l(list(c));
572  
    int n = 0;
573  
    for (Concept x : list(c)) if (checkConceptFields(x, params)) ++n;
574  
    ret n;
575  
  }
576  
577  
  int countConcepts() {
578  
    ret l(concepts);
579  
  }
580  
  
581  
  synchronized L<IConceptIndex> clonedConceptIndices() {
582  
    ret cloneList(conceptIndices);
583  
  }
584  
  
585  
  synchronized void addConceptIndex(IConceptIndex index) {
586  
    if (conceptIndices == null)
587  
      conceptIndices = new L;
588  
    conceptIndices.add(index);
589  
  }
590  
  
591  
  synchronized void removeConceptIndex(IConceptIndex index) {
592  
    if (conceptIndices == null) ret;
593  
    conceptIndices.remove(index);
594  
    if (empty(conceptIndices)) conceptIndices = null;
595  
  }
596  
  
597  
  synchronized void addFieldIndex(Class<? extends Concept> c, S field, IFieldIndex index) {
598  
    if (fieldIndices == null)
599  
      fieldIndices = new HashMap;
600  
    Map<S, IFieldIndex> map = fieldIndices.get(c);
601  
    if (map == null)
602  
      fieldIndices.put(c, map = new HashMap);
603  
    map.put(field, index);
604  
  }
605  
  
606  
  synchronized void removeFieldIndex(Class<? extends Concept> c, S field, IFieldIndex index) {
607  
    Map<S, IFieldIndex> map = mapGet(fieldIndices, c);
608  
    mapRemove(map, field);
609  
  }
610  
  
611  
  synchronized IFieldIndex getFieldIndex(Class<? extends Concept> c, S field) {
612  
    if (fieldIndices == null) null;
613  
    Map<S, IFieldIndex> map = fieldIndices.get(c);
614  
    ret map == null ? null : map.get(field);
615  
  }
616  
  
617  
  synchronized IFieldIndex getAnyIndexForClass(Class<? extends Concept> c) {
618  
    ret firstValue(fieldIndices?.get(c));
619  
  }
620  
  
621  
  synchronized void addCIFieldIndex(Class<? extends Concept> c, S field, IFieldIndex index) {
622  
    if (ciFieldIndices == null)
623  
      ciFieldIndices = new HashMap;
624  
    Map<S, IFieldIndex> map = ciFieldIndices.get(c);
625  
    if (map == null)
626  
      ciFieldIndices.put(c, map = new HashMap);
627  
    map.put(field, index);
628  
  }
629  
  
630  
  synchronized void removeCIFieldIndex(Class<? extends Concept> c, S field) {
631  
    Map<S, IFieldIndex> map = mapGet(ciFieldIndices, c);
632  
    mapRemove(map, field);
633  
  }
634  
  
635  
  synchronized IFieldIndex getCIFieldIndex(Class<? extends Concept> c, S field) {
636  
    if (ciFieldIndices == null) null;
637  
    Map<S, IFieldIndex> map = ciFieldIndices.get(c);
638  
    ret map == null ? null : map.get(field);
639  
  }
640  
  
641  
  // inter-process methods
642  
  
643  
  RC xnew(S name, O... values) {
644  
    ret new RC(cnew(name, values));
645  
  }
646  
  
647  
  void xset(long id, S field, O value) {
648  
    xset(new RC(id), field, value);
649  
  }
650  
  
651  
  void xset(RC c, S field, O value) {
652  
    if (value instanceof RC)
653  
      value = getConcept((RC) value);
654  
    cset(getConcept(c), field, value);
655  
  }
656  
  
657  
  O xget(long id, S field) {
658  
    ret xget(new RC(id), field);
659  
  }
660  
  
661  
  O xget(RC c, S field) {
662  
    ret xgetPost(cget(getConcept(c), field));
663  
  }
664  
  
665  
  O xgetPost(O o) {
666  
    o = deref(o);
667  
    if (o instanceof Concept)
668  
      ret new RC((Concept) o);
669  
    ret o;
670  
  }
671  
  
672  
  void xdelete(long id) {
673  
    xdelete(new RC(id));
674  
  }
675  
  
676  
  void xdelete(RC c) {
677  
    getConcept(c).delete();
678  
  }
679  
  
680  
  void xdelete(L<RC> l) {
681  
    for (RC c : l)
682  
      xdelete(c);
683  
  }
684  
  
685  
  L<RC> xlist() {
686  
    ret map("toPassRef", allConcepts());
687  
  }
688  
  
689  
  L<RC> xlist(S className) {
690  
    ret map("toPassRef", conceptsOfType(className));
691  
  }
692  
  
693  
  bool isTransient() { ret eq(programID, "-"); }
694  
  
695  
  S xfullgrab() {
696  
    if (noXFullGrab) fail("no xfullgrab (DB too large)");
697  
    lock lock();
698  
    if (changes == changesWritten && !isTransient())
699  
      ret loadConceptsStructure(programID);
700  
    ret fullStructure();
701  
  }
702  
  
703  
  /* dev.
704  
  Either<File, byte[]> xfullgrabGZipped() {
705  
    lock lock();
706  
    if (changes == changesWritten && !isTransient())
707  
      ret loadConceptsStructure(programID);
708  
    ret fullStructure();
709  
  }*/
710  
  
711  
  void xshutdown() {
712  
    // Killing whole VM if someone wants this DB to shut down
713  
    cleanKillVM();
714  
  }
715  
  
716  
  long xchangeCount() { ret changes; }
717  
  int xcount() { ret countConcepts(); }
718  
  
719  
  void register(Concept c) {
720  
    ping();
721  
    if (c._concepts == this) ret;
722  
    if (c._concepts != null) fail("Can't re-register");
723  
    c.id = internalID();
724  
    c.created = now();
725  
    if (modifyOnCreate) c._setModified(c.created);
726  
    register_phase2(c);
727  
    vmBus_send conceptCreated(c);
728  
    fireChange(new ConceptCreate(c));
729  
  }
730  
  
731  
  // also called by replaceConceptAndUpdateRefs
732  
  void register_phase2(Concept c) {
733  
    c._concepts = this;
734  
    concepts.put((long) c.id, c);
735  
    for (Concept.Ref r : c._refs())
736  
      r.index();
737  
    c.change();
738  
    c._onRegistered();
739  
  }
740  
  
741  
  void registerKeepingID(Concept c) {
742  
    if (c._concepts == this) ret;
743  
    if (c._concepts != null) fail("Can't re-register");
744  
    c._concepts = this;
745  
    concepts.put((long) c.id, c);
746  
    c.change();
747  
  }
748  
  
749  
  void conceptChanged(Concept c) {
750  
    fireChange(new ConceptChange(c));
751  
    if (conceptIndices != null)
752  
      for (IConceptIndex index : clonedConceptIndices())
753  
        index.update(c);
754  
  }
755  
  
756  
  bool hasUnsavedData() {
757  
    ret changes != changesWritten || savingConcepts;
758  
  }
759  
  
760  
  synchronized O miscMapGet(O key) {
761  
    ret mapGet(miscMap, key);
762  
  }
763  
  
764  
  synchronized O miscMapPut(O key, O value) {
765  
    if (miscMap == null) miscMap = new Map;
766  
    ret miscMap.put(key, value);
767  
  }
768  
  
769  
  synchronized void miscMapRemove(O key) {
770  
    mapRemove(miscMap, key);
771  
  }
772  
  
773  
  // Note: auto-typing can fool you, make sure create returns
774  
  // a wide enough type
775  
  synchronized <A> A miscMapGetOrCreate(O key, IF0<A> create) {
776  
    if (containsKey(miscMap, key)) ret (A) miscMap.get(key);
777  
    A value = create!;
778  
    miscMapPut(key, value);
779  
    ret value;
780  
  }
781  
  
782  
  void setParent(Concepts parent) {
783  
    this.parent = parent;
784  
  }
785  
  
786  
  void fireChange(ConceptsChange change) {
787  
    if (change == null) ret;
788  
    pcallFAll(onChange, change);
789  
    fireLegacyChangeEvent();
790  
  }
791  
  
792  
  void addChangeListener aka onChange(IVF1<ConceptsChange> l) {
793  
    syncAdd(onChange, l);
794  
  }
795  
  
796  
  void removeChangeListener(IVF1<ConceptsChange> l) {
797  
    syncRemove(onChange, l);
798  
  }
799  
  
800  
  void addPreSave(Runnable r) {
801  
    preSave = syncAddOrCreate(preSave, r);
802  
  }
803  
  
804  
  toString {
805  
    ret nConcepts(concepts) + " (" + programID + ", hash: " + identityHashCode(this) + ")";
806  
  }
807  
} // end of Concepts
808  
809  
sclass Concept extends DynamicObject {
810  
  transient Concepts _concepts; // Where we belong
811  
  long id;
812  
  long created, _modified;
813  
  ifdef ConceptRefsField
814  
  L<Ref> refs;
815  
  endifdef
816  
  L<Ref> backRefs;
817  
  
818  
  // used only internally (cnew)
819  
  *(S className) {
820  
    super(className);
821  
    _created();
822  
  }
823  
  
824  
  *() {
825  
    if (!_loading()) {
826  
      //className = shortClassName(this); // XXX - necessary?
827  
      //print("New concept of type " + className);
828  
      _created();
829  
    }
830  
  }
831  
  
832  
  *(bool unlisted) {
833  
    if (!unlisted) _created();
834  
  }
835  
  
836  
  toString {
837  
    ret shortDynamicClassName(this) + " " + id;
838  
  }
839  
  
840  
  static bool loading() { ret _loading(); }
841  
  static bool _loading() { ret dynamicObjectIsLoading(); }
842  
843  
  void _created() {
844  
    if (!concepts_unlistedByDefault && !eq(concepts_unlisted!, true))
845  
      db_mainConcepts().register(this);
846  
  }
847  
  
848  
  // base class + required interface. experimental
849  
  persistable class TypedRef<A extends Concept, B> extends Ref<A> {
850  
    //Class<B> aType;
851  
    Class<B> bType;
852  
    
853  
    *(Class<B> *bType) {}
854  
    *(Class<B> *bType, B value) { set((A) value); }
855  
    *(B value) { set((A) value); }
856  
    
857  
    public bool set(A a) {
858  
      ret super.set(checkValue(a));
859  
    }
860  
    
861  
    void check { checkValue(get()); }
862  
    
863  
    <C> C checkValue(C a) {
864  
      if (bType != null && a != null)
865  
        assertIsInstance(a, bType);
866  
      ret a;
867  
    }
868  
    
869  
    B b() { ret (B) value; }
870  
  }
871  
  
872  
  class Ref<A extends Concept> implements IRef<A> {
873  
    A value;
874  
    
875  
    *() {
876  
      if (!dynamicObjectIsLoading())
877  
        registerRef();
878  
    }
879  
    
880  
    void registerRef {
881  
      vmBus_send registeringConceptRef(this);
882  
    }
883  
    
884  
    *(A *value) {
885  
      registerRef();
886  
      index();
887  
    }
888  
    
889  
    // get owning concept (source)
890  
    Concept concept() {
891  
      ret Concept.this;
892  
    }
893  
    
894  
    // get target
895  
    public A get() { ret value; }
896  
    public bool has() { ret value != null; }
897  
    
898  
    bool set(A a) {
899  
      if (a == value) false;
900  
      unindex();
901  
      value = a;
902  
      index();
903  
      change();
904  
      true;
905  
    }
906  
    
907  
    void setIfEmpty(A a) {
908  
      if (!has()) set(a);
909  
    }
910  
    
911  
    public void set(Ref<A> ref) { set(ref.get()); }
912  
    public void clear() { set((A) null); }
913  
    
914  
    bool validRef() {
915  
      ret value != null && _concepts != null && _concepts == value._concepts;
916  
    }
917  
    
918  
    // TODO: sync all the indexing and unindexing!?
919  
    void index() { 
920  
      if (validRef()) {
921  
        value._addBackRef(this);
922  
        change();
923  
      }
924  
    }
925  
926  
    Ref<A> unindex() {
927  
      if (validRef()) {
928  
        value._removeBackRef(this);
929  
        change();
930  
      }
931  
      this;
932  
    }
933  
    
934  
    void unindexAndDrop {
935  
      unindex();
936  
      _removeRef(this);
937  
    }
938  
    
939  
    void change() {
940  
      Concept.this.change();
941  
    }
942  
    
943  
    toString { ret ifdef ConceptRef_markInToString "Concept.Ref " + endifdef
944  
    str(value); }
945  
  }
946  
  
947  
  class RefL<A extends Concept> extends AbstractList<A> {
948  
    new L<Ref<A>> l;
949  
    
950  
    *() {}
951  
    *(L<A> l) { replaceWithList(l); }
952  
    
953  
    public void clear {
954  
      while (!isEmpty()) removeLast(this);
955  
    }
956  
    
957  
    public void replaceWithList(L<A> l) {
958  
      clear();
959  
      fOr (A a : l) add(a);
960  
    }
961  
    
962  
    public A set(int i, A o) {
963  
      Ref<A> ref = syncGet(l, i);
964  
      A prev = ref!;
965  
      ref.set(o);
966  
      ret prev;
967  
    }
968  
    
969  
    public void add(int i, A o) {
970  
      syncAdd(l, i, new Ref(o));
971  
    }
972  
    
973  
    public A get(int i) {
974  
      ret syncGet(l, i)!;
975  
    }
976  
    
977  
    public A remove(int i) {
978  
      ret syncRemove(l, i)!;
979  
    }
980  
    
981  
    public int size() {
982  
      ret syncL(l);
983  
    }
984  
    
985  
    public bool contains(O o) {
986  
      if (o instanceof Concept)
987  
        for (Ref<A> r : l) if (eq(r!, o)) true;
988  
      ret super.contains(o);
989  
    }
990  
  }
991  
  
992  
  void delete() {
993  
    //name = "[defunct " + name + "]";
994  
    //defunct = true;
995  
    //energy = 0;
996  
    
997  
    // clean refs
998  
    
999  
    fOr (Ref r : _refs())
1000  
      r.unindex();
1001  
    ifdef ConceptRefsField
1002  
    refs = null;
1003  
    endifdef
1004  
    
1005  
    // set back refs to null
1006  
    
1007  
    for (Ref r : cloneList(backRefs))
1008  
      r.set((Concept) null);
1009  
    backRefs = null;
1010  
    
1011  
    var _concepts = this._concepts;
1012  
    if (_concepts != null) {
1013  
      _concepts.concepts.remove(id);
1014  
      _concepts.fireChange(new ConceptDelete(id, this));
1015  
      if (_concepts.conceptIndices != null)
1016  
        for (IConceptIndex index : _concepts.conceptIndices)
1017  
          index.remove(this);
1018  
      this._concepts = null;
1019  
    }
1020  
    id = 0;
1021  
  }
1022  
  
1023  
  BaseXRef export() {
1024  
    ret new BaseXRef(_concepts.progID(), id);
1025  
  }
1026  
  
1027  
  // notice system of a change in this object
1028  
  void change() {
1029  
    _setModified(now());
1030  
    _change_withoutUpdatingModifiedField();
1031  
  }
1032  
  
1033  
  void _setModified(long modified) {
1034  
    _modified = modified;
1035  
  }
1036  
  
1037  
  void _change_withoutUpdatingModifiedField() {
1038  
    if (_concepts != null) _concepts.conceptChanged(this);
1039  
  }
1040  
  
1041  
  void _change() { change(); }
1042  
  
1043  
  S _programID() {
1044  
    ret _concepts == null ? getDBProgramID() : _concepts.progID();
1045  
  }
1046  
  
1047  
  // overridable
1048  
  
1049  
  void _addBackRef(Concept.Ref ref) {
1050  
    backRefs = addDyn_quickSync(backRefs, ref);
1051  
    _backRefsModified();
1052  
  }
1053  
  
1054  
  void _backRefsModified {
1055  
    if (_concepts != null && _concepts.modifyOnBackRef) change();
1056  
  }
1057  
  
1058  
  void _removeBackRef(Concept.Ref ref) {
1059  
    backRefs = removeDyn_quickSync(backRefs, ref);
1060  
    _backRefsModified();
1061  
  }
1062  
  
1063  
  void _removeRef(Concept.Ref ref) {
1064  
    ifdef ConceptRefsField
1065  
    refs = removeDyn_quickSync(refs, ref);
1066  
    endifdef
1067  
  }
1068  
  
1069  
  int _backRefCount() { ret syncL(backRefs); }
1070  
  
1071  
  // convenience methods
1072  
  
1073  
  void _setField aka setField(S field, O value) {
1074  
    cset(this, field, value);
1075  
  }
1076  
  
1077  
  bool setField_trueIfChanged(S field, O value) {
1078  
    ret cset(this, field, value) != 0;
1079  
  }
1080  
  
1081  
  <A> A setFieldAndReturn(S field, A value) {
1082  
    setField(field, value);
1083  
    ret value;
1084  
  }
1085  
  
1086  
  void _setFields aka setFields(O... values) {
1087  
    cset(this, values);
1088  
  }
1089  
  
1090  
  Concepts concepts() { ret _concepts; }
1091  
  
1092  
  bool isDeleted() { ret id == 0; }
1093  
  
1094  
  void _doneLoading2() {
1095  
    Map<S, FieldMigration> map = _fieldMigrations();
1096  
    if (map != null) for (S oldField, FieldMigration m : map)
1097  
      crenameField_noOverwrite(this, oldField, m.newField);
1098  
  }
1099  
  
1100  
  srecord FieldMigration(S newField) {}
1101  
  
1102  
  // value is 
1103  
  Map<S, FieldMigration> _fieldMigrations() { null; }
1104  
  
1105  
  // new wrapper to get a copy of the refs list
1106  
  // so we can eventually drop the refs field
1107  
  Cl<Ref> _refs() {
1108  
    ret scanConceptForRefs(this);
1109  
  }
1110  
  
1111  
  ifdef ConceptRefsField
1112  
  // for debugging
1113  
  L<Ref> _rawRefsField() {
1114  
    ret refs;
1115  
  }
1116  
  endifdef
1117  
  
1118  
  Concepts _concepts() { ret _concepts; }
1119  
  
1120  
  bool _conceptsDefunct() { ret _concepts != null && _concepts.defunct; }
1121  
  bool _conceptsDefunctOrUnregistered() { ret _concepts == null || _concepts.defunct; }
1122  
  
1123  
  // allow refs to do magic stuff?
1124  
  void _onRegistered {
1125  
    /*for (Ref ref : _refs())
1126  
      refs._onRegistered();*/
1127  
  }
1128  
  
1129  
  !include #1031423 // Concept convenience methods
1130  
} // end of Concept
1131  
1132  
// remote reference (for inter-process communication or
1133  
// external databases). Formerly "PassRef".
1134  
// prepared for string ids if we do them later
1135  
sclass RC {
1136  
  transient O owner;
1137  
  S id;
1138  
  
1139  
  *() {} // make serialisation happy
1140  
  *(long id) { this.id = str(id); }
1141  
  *(O owner, long id) { this.id = str(id); this.owner = owner; }
1142  
  *(Concept c) { this(c.id); }
1143  
  long longID() { ret parseLong(id); }
1144  
  
1145  
  public S toString() {
1146  
    ret id;
1147  
  }
1148  
}
1149  
1150  
// Reference to a concept in another program
1151  
sclass BaseXRef {
1152  
  S programID;
1153  
  long id;
1154  
    
1155  
  *() {}
1156  
  *(S *programID, long *id) {}
1157  
  
1158  
  public bool equals(O o) {
1159  
    if (!(o instanceof BaseXRef)) false;
1160  
    BaseXRef r = cast o;
1161  
    ret eq(programID, r.programID) && eq(id, r.id);
1162  
  }
1163  
  
1164  
  public int hashCode() {
1165  
    ret programID.hashCode() + (int) id;
1166  
  }
1167  
}
1168  
1169  
// BaseXRef as a concept
1170  
sclass XRef extends Concept {
1171  
  BaseXRef ref;
1172  
  
1173  
  *() {}
1174  
  *(BaseXRef *ref) { _doneLoading2(); }
1175  
  
1176  
  // after we have been added to concepts
1177  
  void _doneLoading2() {
1178  
    getIndex().put(ref, this);
1179  
  }
1180  
    
1181  
  HashMap<BaseXRef, XRef> getIndex() {
1182  
    ret getXRefIndex(_concepts);
1183  
  }
1184  
}
1185  
1186  
static synchronized HashMap<BaseXRef, XRef> getXRefIndex(Concepts concepts) {
1187  
  HashMap cache = (HashMap) concepts.perClassData().get(XRef.class);
1188  
  if (cache == null)
1189  
    concepts.perClassData.put(XRef.class, cache = new HashMap);
1190  
  ret cache;
1191  
}
1192  
1193  
// uses mainConcepts
1194  
static XRef lookupOrCreateXRef(BaseXRef ref) {
1195  
  XRef xref = getXRefIndex(db_mainConcepts()).get(ref);
1196  
  if (xref == null)
1197  
    xref = new XRef(ref);
1198  
  ret xref;
1199  
}
1200  
1201  
// define standard concept functions to use main concepts
1202  
1203  
// Now in db_mainConcepts()
1204  
/*static void cleanMeUp_concepts() {
1205  
  if (db_mainConcepts() != null) db_mainConcepts().cleanMeUp();
1206  
  // mainConcepts = null; // TODO
1207  
}*/
1208  
1209  
svoid loadAndAutoSaveConcepts {
1210  
  db_mainConcepts().persist();
1211  
}
1212  
1213  
svoid loadAndAutoSaveConcepts(int interval) {
1214  
  db_mainConcepts().persist(interval);
1215  
}
1216  
1217  
static RC toPassRef(Concept c) {
1218  
  ret new RC(c);
1219  
}
1220  
1221  
// so we can instantiate the program to run as a bare DB bot
1222  
please include function bareDBMode.
1223  
1224  
// so unstructuring Concepts works
1225  
please include function dynamicObjectIsLoading_threadLocal.
1226  
1227  
svoid concepts_setUnlistedByDefault(bool b) {
1228  
  concepts_unlistedByDefault = b;
1229  
}

Author comment

Began life as a copy of #1004863

download  show line numbers  debug dex  old transpilations   

Travelled to 3 computer(s): bhatertpkbcr, mowyntqkapby, mqqgnosmbjvj

No comments. add comment

Snippet ID: #1034278
Snippet name: "Concepts" (persistent Java objects, backup)
Eternal ID of this version: #1034278/1
Text MD5: ff8c267546b7356df1a647629c8d8620
Author: stefan
Category: javax
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2022-01-31 19:45:15
Source code size: 33834 bytes / 1229 lines
Pitched / IR pitched: No / No
Views / Downloads: 97 / 109
Referenced in: [show references]