private void invokeCallbacks(Callback<Void>[] callbacks, Throwable e) { for (Callback<Void> callback : callbacks) { try { callback.onDone(null, e); } catch (Throwable e2) { Log.error("Transaction callback error", e2); } } }
private void persist() { while (!Thread.interrupted()) { try { persistData(); } catch (Exception e1) { Log.error("Failed to persist data!", e1); } if (data.active.get()) { try { Thread.sleep(100); } catch (InterruptedException e) { } } else { try { persistData(); } catch (Exception e1) { Log.error("Failed to persist data!", e1); } return; } } }
/** * ACID transactional semantics:<br> * - Atomicity with automatic rollback in case of exception,<br> * - Consistency - only with constraints enforced programmatically inside transaction,<br> * - Isolation is serializable (with global lock),<br> * - Durability through on-commit callbacks.<br> */ public void transaction(Runnable transaction, boolean readOnly, Callback<Void> txCallback) { globalLock(); data.txIdCounter.set(data.ids.get()); data.txChanges.clear(); data.txInsertions.clear(); data.txReadonly.set(readOnly); data.insideTx.set(true); boolean success = false; try { transaction.run(); success = true; } catch (Throwable e) { if (SuccessException.isSuccess(e)) { success = true; throw U.rte(e); } else { Log.error("Error in transaction, rolling back", e); txRollback(); if (txCallback != null) { txCallback.onDone(null, e); txCallback = null; } } } finally { data.txChanges.clear(); data.txInsertions.clear(); data.insideTx.set(false); if (persistor != null) { if (success && txCallback != null) { data.txCallbacks.add(txCallback); } } else { if (success && txCallback != null) { txCallback.onDone(null, null); } } globalUnlock(); } }
public void loadFrom(InputStream in) { globalLock(); try { data.data.clear(); BufferedReader reader = new BufferedReader(new InputStreamReader(in)); String line = reader.readLine(); byte[] bytes = line.getBytes(); U.must(line != null, "Missing meta-data at the first line in the database file!"); Map<String, Object> meta = U.map(); data.serializer.deserialize(bytes, meta); Log.info( "Database meta-data", META_TIMESTAMP, meta.get(META_TIMESTAMP), META_UPTIME, meta.get(META_UPTIME)); while ((line = reader.readLine()) != null) { bytes = line.getBytes(); Map<String, Object> map = U.map(); data.serializer.deserialize(bytes, map); Object idObj = map.get(ID); U.must(idObj != null, "Found DB record without ID: %s", line); long id = Cls.convert(idObj, Long.class); String className = ((String) map.get("_class")); String[] nameParts = className.split("\\."); String simpleName = nameParts[nameParts.length - 1]; List<Class<?>> classes = Scan.byName(simpleName, null, null); if (classes.size() == 1) { Class<?> type = classes.get(0); data.data.put(id, new Rec(type, bytes)); if (id > data.ids.get()) { data.ids.set(id); } } else { if (classes.isEmpty()) { Log.error("Cannot find the class of a DB record!", "id", id, "class", className); } else { Log.error("Found more than 1 class of a DB record!", "id", id, "class", className); } } } data.prevData = new ConcurrentSkipListMap<Long, Rec>(data.data); reader.close(); } catch (IOException e) { throw new RuntimeException("Cannot load database!", e); } finally { globalUnlock(); } }