/**
* This file is part of ObjectFabric (http://objectfabric.org).
*
* ObjectFabric is licensed under the Apache License, Version 2.0, the terms
* of which may be found at http://www.apache.org/licenses/LICENSE-2.0.html.
*
* Copyright ObjectFabric Inc.
*
* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
* WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.objectfabric;
import org.objectfabric.Actor.Message;
import org.objectfabric.Resource.NewBlock;
import org.objectfabric.SQLiteLoop.Query;
import com.almworks.sqlite4java.SQLiteConnection;
import com.almworks.sqlite4java.SQLiteException;
import com.almworks.sqlite4java.SQLiteStatement;
final class SQLiteClock extends Clock {
private final SQLite _location;
SQLiteClock(Watcher watcher, SQLite location) {
super(watcher);
_location = location;
}
@Override
void writing(Resources resources) {
final long[][] loaded = new long[resources.size()][];
for (int i = 0; i < resources.size(); i++)
loaded[i] = Platform.get().clone(resources.get(i).loaded());
_location.writer().add(new Query() {
@Override
void run(SQLiteConnection db) throws SQLiteException {
SQLiteStatement st = db.prepare(Shared.SELECT_CLOCKS);
Peer peer = null;
long time = 0, object = 0;
boolean foundOne = false;
try {
while (st.step()) {
peer = Peer.get(new UID(st.columnBlob(0)));
time = st.columnLong(1);
object = st.columnLong(2);
long tick = Tick.get(peer.index(), time);
if (upToDate(loaded, tick)) {
foundOne = true;
break;
}
}
} finally {
st.dispose();
}
if (!foundOne) {
peer = Peer.get(new UID(Platform.get().newUID()));
time = Clock.time(0, false);
object = 0;
}
_location.writer().walks().put(peer, peer);
final Peer peer_ = peer;
final long time_ = time;
final long object_ = object;
watcher().actor().addAndRun(new Message() {
@Override
void run() {
watcher().clock().init(peer_, time(time_, true), object_);
}
});
}
});
}
private final boolean upToDate(long[][] loaded, long tick) {
for (int i = 0; i < loaded.length; i++)
if (!Tick.happenedBefore(tick, loaded[i]))
return false;
return true;
}
@Override
void commit() {
if (Debug.ENABLED)
Debug.assertion((peer() == null) == (blocks().size() == 0));
if (blocks().size() > 0) {
final NewBlock[] blocks = new NewBlock[blocks().size()];
final Buff[][] duplicates = new Buff[blocks.length][];
for (int i = blocks.length - 1; i >= 0; i--) {
blocks[i] = blocks().removeLast();
duplicates[i] = new Buff[blocks[i].Buffs.length];
for (int d = 0; d < blocks[i].Buffs.length; d++) {
duplicates[i][d] = blocks[i].Buffs[d].duplicate();
if (Debug.THREADS)
ThreadAssert.exchangeGive(duplicates, duplicates[i][d]);
}
}
final Peer peer = peer();
final long time = time();
final long object = object();
init(null, 0, 0);
_location.writer().add(new Query() {
@Override
void run(SQLiteConnection db) throws SQLiteException {
if (Debug.THREADS)
ThreadAssert.exchangeTake(duplicates);
SQLiteStatement st = db.prepare(Shared.REPLACE_CLOCK);
try {
st.bind(1, peer.uid());
st.bind(2, time);
st.bind(3, object);
st.step();
} finally {
st.dispose();
}
for (int i = 0; i < blocks.length; i++) {
_location.queue().write(db, blocks[i].Resource.uri(), blocks[i].Tick, duplicates[i], blocks[i].Removals);
if (Debug.THREADS)
for (int d = 0; d < duplicates[i].length; d++)
ThreadAssert.exchangeGive(watcher().actor(), duplicates[i][d]);
}
_location.writer().walks().remove(peer);
}
@Override
void ack() {
watcher().actor().addAndRun(new Message() {
@Override
void run() {
for (int i = 0; i < blocks.length; i++) {
SQLiteView view = (SQLiteView) blocks[i].Resource.uri().getOrCreate(_location);
view.add(blocks[i].Tick, blocks[i].Removals);
publish(blocks[i].Resource, blocks[i].Tick, duplicates[i], blocks[i].Removals, _location);
for (int d = 0; d < duplicates[i].length; d++)
duplicates[i][d].recycle();
blocks[i].Resource.ack(_location, blocks[i].Tick);
}
}
});
}
});
}
}
}