First version of data persistence architecture
This commit is contained in:
parent
54b31ac7d1
commit
5b7c6a857a
24
pom.xml
24
pom.xml
|
@ -69,6 +69,30 @@
|
|||
<artifactId>gethostname4j</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>es.blackleg</groupId>
|
||||
<artifactId>jlibnotify</artifactId>
|
||||
<version>1.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.hypfvieh</groupId>
|
||||
<artifactId>dbus-java-core</artifactId>
|
||||
<version>5.1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<!-- Unixsocket support using native unix socket implementation of Java (since Java 16) -->
|
||||
<dependency>
|
||||
<groupId>com.github.hypfvieh</groupId>
|
||||
<artifactId>dbus-java-transport-native-unixsocket</artifactId>
|
||||
<version>5.1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Add this if you want support for TCP based DBus connections -->
|
||||
<dependency>
|
||||
<groupId>com.github.hypfvieh</groupId>
|
||||
<artifactId>dbus-java-transport-tcp</artifactId>
|
||||
<version>5.1.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -11,6 +11,12 @@ import com.offbynull.portmapper.gateways.process.ProcessGateway;
|
|||
import com.offbynull.portmapper.mapper.MappedPort;
|
||||
import com.offbynull.portmapper.mapper.PortMapper;
|
||||
import com.offbynull.portmapper.mapper.PortType;
|
||||
import es.blackleg.jlibnotify.JLibnotify;
|
||||
import es.blackleg.jlibnotify.JLibnotifyNotification;
|
||||
import es.blackleg.jlibnotify.core.DefaultJLibnotify;
|
||||
import es.blackleg.jlibnotify.core.DefaultJLibnotifyLoader;
|
||||
import es.blackleg.jlibnotify.exception.JLibnotifyInitException;
|
||||
import es.blackleg.jlibnotify.exception.JLibnotifyLoadException;
|
||||
import jnr.ffi.Platform;
|
||||
import moe.nekojimi.friendcloud.filesystem.FUSEAccess;
|
||||
import moe.nekojimi.friendcloud.network.PeerConnection;
|
||||
|
@ -18,8 +24,10 @@ import moe.nekojimi.friendcloud.network.requests.ObjectListRequest;
|
|||
import moe.nekojimi.friendcloud.objects.NetworkFile;
|
||||
import moe.nekojimi.friendcloud.objects.NetworkObject;
|
||||
import moe.nekojimi.friendcloud.objects.Peer;
|
||||
import moe.nekojimi.friendcloud.objects.PeerFileState;
|
||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||
import moe.nekojimi.friendcloud.storage.Model;
|
||||
import moe.nekojimi.friendcloud.storage.StupidJSONFileStore;
|
||||
import moe.nekojimi.friendcloud.tasks.JoinNetworkTask;
|
||||
import org.slf4j.simple.SimpleLogger;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -28,6 +36,7 @@ import java.net.*;
|
|||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.*;
|
||||
|
@ -37,7 +46,7 @@ public class Main
|
|||
private static Main instance;
|
||||
|
||||
@Parameter(names="-share")
|
||||
private List<String> sharedFiles = new ArrayList<>();
|
||||
private List<String> sharedFilePaths = new ArrayList<>();
|
||||
|
||||
@Parameter(names="-known-peer")
|
||||
private List<String> knownPeers = new ArrayList<>();
|
||||
|
@ -48,13 +57,15 @@ public class Main
|
|||
@Parameter(names="-no-upnp")
|
||||
private boolean noUpnp = false;
|
||||
|
||||
@Parameter(names="-create-network")
|
||||
private boolean createNetwork = false;
|
||||
|
||||
// @Parameter(names="-file")
|
||||
|
||||
private ConnectionManager connectionManager;
|
||||
|
||||
private final ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(16);
|
||||
|
||||
private final FUSEAccess fuseAccess = new FUSEAccess();
|
||||
private final Model model = new Model(new StupidJSONFileStore(new File("storage")));
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
|
@ -66,7 +77,7 @@ public class Main
|
|||
try
|
||||
{
|
||||
instance.run();
|
||||
} catch (IOException | InterruptedException e)
|
||||
} catch (IOException | InterruptedException | JLibnotifyLoadException | JLibnotifyInitException e)
|
||||
{
|
||||
e.printStackTrace(System.err);
|
||||
try
|
||||
|
@ -81,7 +92,7 @@ public class Main
|
|||
// TestMessage.SearchRequest request = TestMessage.SearchRequest.newBuilder().setQuery("bees!").setPageNumber(316).setResultsPerPage(42069).build();
|
||||
}
|
||||
|
||||
private void run() throws IOException, InterruptedException
|
||||
private void run() throws IOException, InterruptedException, JLibnotifyLoadException, JLibnotifyInitException
|
||||
{
|
||||
connectionManager = new ConnectionManager(tcpPort);
|
||||
|
||||
|
@ -111,14 +122,20 @@ public class Main
|
|||
}
|
||||
}));
|
||||
|
||||
connectionManager.addNewConnectionConsumer(this::resquestCompleteState);
|
||||
DefaultJLibnotify libnotify = (DefaultJLibnotify) DefaultJLibnotifyLoader.init().load();
|
||||
libnotify.init("FriendCloud");
|
||||
JLibnotifyNotification notification = libnotify.createNotification("Holy balls a notification!", "Woah!!!", "dialog-information");
|
||||
notification.show();
|
||||
|
||||
connectionManager.addNewConnectionConsumer(this::requestCompleteState);
|
||||
|
||||
connectionManager.start();
|
||||
|
||||
String hostname = Hostname.getHostname();
|
||||
Model.getInstance().getSelfPeer().setSystemName(hostname);
|
||||
Model.getInstance().getSelfPeer().setUserName(System.getProperty("user.name") + "-" + tcpPort);
|
||||
model.getSelfPeer().setSystemName(hostname);
|
||||
model.getSelfPeer().setUserName(System.getProperty("user.name") + "-" + tcpPort);
|
||||
addHostAddress(InetAddress.getLocalHost());
|
||||
model.objectChanged(model.getSelfPeer());
|
||||
|
||||
/*
|
||||
Startup procedure:
|
||||
|
@ -133,23 +150,43 @@ public class Main
|
|||
if (!noUpnp)
|
||||
setupIGP();
|
||||
|
||||
for (String sharedFilePath: sharedFiles)
|
||||
Set<File> sharedFiles = new HashSet<>();
|
||||
for (String sharedFilePath: sharedFilePaths)
|
||||
{
|
||||
File file = new File(sharedFilePath);
|
||||
if (file.exists())
|
||||
sharedFiles.add(new File(sharedFilePath));
|
||||
}
|
||||
|
||||
List<NetworkObject> knownFiles = model.listObjects(Set.of(ObjectStatements.ObjectType.OBJECT_TYPE_FILE));
|
||||
|
||||
for (NetworkObject knownFile: knownFiles)
|
||||
{
|
||||
System.out.println("Adding shared network file: " + file.getAbsolutePath());
|
||||
NetworkFile f = (NetworkFile) knownFile;
|
||||
boolean removed = sharedFiles.remove(f.getLocalFile());
|
||||
if (removed)
|
||||
System.out.println("Identified known local file " + f.getObjectID() + " = " + f.getLocalFile());
|
||||
}
|
||||
|
||||
NetworkFile networkFile = (NetworkFile) Model.getInstance().createObjectByType(ObjectStatements.ObjectType.OBJECT_TYPE_FILE);
|
||||
networkFile.updateFromLocalFile(file);
|
||||
for (File sharedFile: sharedFiles)
|
||||
{
|
||||
if (sharedFile.exists())
|
||||
{
|
||||
System.out.println("Adding shared network file: " + sharedFile.getAbsolutePath());
|
||||
|
||||
PeerFileState peerFileState = (PeerFileState) Model.getInstance().createObjectByType(ObjectStatements.ObjectType.OBJECT_TYPE_PEER_FILE_STATE);
|
||||
peerFileState.setNode(Model.getInstance().getSelfPeer());
|
||||
peerFileState.setFile(networkFile);
|
||||
peerFileState.setProgress(100);
|
||||
NetworkFile networkFile = (NetworkFile) model.createObjectByType(ObjectStatements.ObjectType.OBJECT_TYPE_FILE);
|
||||
networkFile.updateFromLocalFile(sharedFile);
|
||||
model.objectChanged(networkFile);
|
||||
|
||||
// PeerFileState peerFileState = (PeerFileState) model.createObjectByType(ObjectStatements.ObjectType.OBJECT_TYPE_PEER_FILE_STATE);
|
||||
// peerFileState.setNode(model.getSelfPeer());
|
||||
// peerFileState.setFile(networkFile);
|
||||
// peerFileState.setProgress(100);
|
||||
// model.objectChanged(peerFileState);
|
||||
}
|
||||
}
|
||||
|
||||
JoinNetworkTask joinNetworkTask = new JoinNetworkTask();
|
||||
executor.submit(joinNetworkTask);
|
||||
|
||||
for (String knownPeerAddress : knownPeers)
|
||||
{
|
||||
String[] split = knownPeerAddress.split(":");
|
||||
|
@ -165,21 +202,7 @@ public class Main
|
|||
URI uri = new URI("tcp", null, address.getHostString(), address.getPort(), null, null, null);
|
||||
PeerConnection nodeConnection = connectionManager.getNodeConnection(uri);
|
||||
|
||||
resquestCompleteState(nodeConnection);
|
||||
|
||||
// objectListFuture.whenComplete((networkObjects, throwable) -> {
|
||||
// for (NetworkObject networkObject: networkObjects)
|
||||
// {
|
||||
// if (networkObject instanceof NetworkFile)
|
||||
// {
|
||||
// System.out.println("Heard about NetworkFile " + networkObject + ", creating download task!");
|
||||
// FileDownloadTask fileDownloadTask = new FileDownloadTask((NetworkFile) networkObject, connectionManager);
|
||||
// executor.submit(fileDownloadTask);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
|
||||
requestCompleteState(nodeConnection);
|
||||
} catch (ConnectException ex)
|
||||
{
|
||||
System.out.println("Couldn't connect to host " + address);
|
||||
|
@ -191,7 +214,7 @@ public class Main
|
|||
}
|
||||
}
|
||||
|
||||
private void resquestCompleteState(PeerConnection nodeConnection)
|
||||
private void requestCompleteState(PeerConnection nodeConnection)
|
||||
{
|
||||
CompletableFuture<List<NetworkObject>> objectListFuture = nodeConnection.makeRequest(new ObjectListRequest(Set.of(
|
||||
ObjectStatements.ObjectType.OBJECT_TYPE_FILE,
|
||||
|
@ -202,7 +225,7 @@ public class Main
|
|||
private void addHostAddress(InetAddress address)
|
||||
{
|
||||
String host = address.getCanonicalHostName();
|
||||
Peer selfNode = Model.getInstance().getSelfPeer();
|
||||
Peer selfNode = model.getSelfPeer();
|
||||
try
|
||||
{
|
||||
URI uri = new URI("tcp", null, host, tcpPort, null, null, null);
|
||||
|
@ -281,4 +304,9 @@ public class Main
|
|||
{
|
||||
return connectionManager;
|
||||
}
|
||||
|
||||
public Model getModel()
|
||||
{
|
||||
return model;
|
||||
}
|
||||
}
|
|
@ -1,167 +0,0 @@
|
|||
package moe.nekojimi.friendcloud;
|
||||
|
||||
import moe.nekojimi.friendcloud.objects.*;
|
||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Model
|
||||
{
|
||||
private static Model instance = null;
|
||||
public static Model getInstance()
|
||||
{
|
||||
if (instance == null)
|
||||
instance = new Model();
|
||||
return instance;
|
||||
}
|
||||
|
||||
private final Map<NetworkObject.ObjectID, NetworkObject> objects = new HashMap<>();
|
||||
private final int systemID;
|
||||
|
||||
private Peer selfPeer = null;
|
||||
private ObjectChangeRecord currentChange;
|
||||
private final Map<Long, ObjectChangeRecord> changeRecords = new HashMap<>();
|
||||
|
||||
private Model()
|
||||
{
|
||||
Random ran = new Random();
|
||||
systemID = ran.nextInt() & 0x00FFFFFF;
|
||||
}
|
||||
|
||||
public void setSelfPeer(Peer selfPeer)
|
||||
{
|
||||
this.selfPeer = selfPeer;
|
||||
}
|
||||
|
||||
public synchronized Peer getSelfPeer()
|
||||
{
|
||||
// if (selfPeer == null)
|
||||
// selfPeer = (Peer) createObjectByType(ObjectStatements.ObjectType.OBJECT_TYPE_PEER);
|
||||
return selfPeer;
|
||||
}
|
||||
// private Map<Long, Node> nodes = new HashMap<>();
|
||||
|
||||
public synchronized NetworkObject.ObjectID getNextObjectID(ObjectStatements.ObjectType type)
|
||||
{
|
||||
Random ran = new Random();
|
||||
int randomNumber = ran.nextInt();
|
||||
NetworkObject.ObjectID objectID = new NetworkObject.ObjectID(type, systemID, randomNumber);
|
||||
System.out.println("Assigned new object ID: " + objectID);
|
||||
return objectID;
|
||||
}
|
||||
|
||||
public synchronized NetworkObject createObjectByID(NetworkObject.ObjectID id)
|
||||
{
|
||||
ObjectStatements.ObjectType type = id.getType();
|
||||
System.out.println("Creating new object with type: " + type.name());
|
||||
NetworkObject ret = switch (type)
|
||||
{
|
||||
// case UNRECOGNIZED -> ;
|
||||
case OBJECT_TYPE_FILE -> new NetworkFile(id);
|
||||
case OBJECT_TYPE_UNSPECIFIED -> throw new IllegalArgumentException();
|
||||
// case OBJECT_TYPE_USER -> null;
|
||||
case OBJECT_TYPE_FOLDER -> new NetworkFolder(id);
|
||||
case OBJECT_TYPE_PEER -> new Peer(id);
|
||||
case OBJECT_TYPE_PEER_FILE_STATE -> new PeerFileState(id);
|
||||
default -> throw new UnsupportedOperationException("NYI");
|
||||
};
|
||||
objects.put(id, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public synchronized NetworkObject createObjectByType(ObjectStatements.ObjectType type)
|
||||
{
|
||||
return createObjectByID(getNextObjectID(type));
|
||||
}
|
||||
|
||||
public synchronized NetworkObject getOrCreateObject(NetworkObject.ObjectID id)
|
||||
{
|
||||
if (!objects.containsKey(id))
|
||||
{
|
||||
objects.put(id, createObjectByID(id));
|
||||
}
|
||||
return objects.get(id);
|
||||
}
|
||||
|
||||
public synchronized List<NetworkObject.ObjectID> listObjects(Set<ObjectStatements.ObjectType> types)
|
||||
{
|
||||
return objects.keySet().stream().filter((id)->(types.contains(id.getType()))).toList();
|
||||
}
|
||||
|
||||
public synchronized NetworkObject getObject(NetworkObject.ObjectID objectID)
|
||||
{
|
||||
return objects.get(objectID);
|
||||
}
|
||||
|
||||
public synchronized List<NetworkFSNode> listFSNodes(String path)
|
||||
{
|
||||
//TODO: dumbest algorithm in the world
|
||||
|
||||
List<NetworkFSNode> ret = new ArrayList<>();
|
||||
for (NetworkObject.ObjectID nodeID : listObjects(Set.of(ObjectStatements.ObjectType.OBJECT_TYPE_FILE, ObjectStatements.ObjectType.OBJECT_TYPE_FOLDER)))
|
||||
{
|
||||
NetworkFSNode fsNode = (NetworkFSNode) getObject(nodeID);
|
||||
String networkPath = fsNode.getNetworkPath();
|
||||
if (networkPath.substring(0, networkPath.lastIndexOf("/")+1).equals(path))
|
||||
ret.add(fsNode);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public synchronized NetworkFSNode getFSNode(String path)
|
||||
{
|
||||
for (NetworkObject.ObjectID nodeID : listObjects(Set.of(ObjectStatements.ObjectType.OBJECT_TYPE_FILE, ObjectStatements.ObjectType.OBJECT_TYPE_FOLDER)))
|
||||
{
|
||||
NetworkFSNode fsNode = (NetworkFSNode) getObject(nodeID);
|
||||
String networkPath = fsNode.getNetworkPath();
|
||||
if (networkPath.equals(path))
|
||||
return fsNode;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public synchronized void addChangeRecord(ObjectChangeRecord record)
|
||||
{
|
||||
changeRecords.put(record.getChangeID(), record);
|
||||
}
|
||||
|
||||
public ObjectChangeRecord getChangeRecord(long id)
|
||||
{
|
||||
return changeRecords.get(id);
|
||||
}
|
||||
|
||||
public void applyChangeRecord(ObjectChangeRecord record)
|
||||
{
|
||||
if (!record.getChangeHeads().contains(currentChange))
|
||||
throw new IllegalStateException("Change does not apply! Valid change heads=" + record.getChangeHeads() + ", we are in state " + currentChange.getChangeID());
|
||||
if (!changeRecords.containsKey(record.getChangeID()))
|
||||
addChangeRecord(record);
|
||||
|
||||
|
||||
// if (record == null)
|
||||
// throw new IllegalArgumentException("Cannot apply unknown change!");
|
||||
}
|
||||
|
||||
public Set<ObjectChangeRecord> getChangeHeads()
|
||||
{
|
||||
// stupid algorithm - start with all of the changes, then remove the ones that are referenced by something
|
||||
// TODO: better algorithm
|
||||
Set<ObjectChangeRecord> ret = new HashSet<>(changeRecords.values());
|
||||
for (ObjectChangeRecord record : changeRecords.values())
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public Set<Peer> listOtherPeers()
|
||||
{
|
||||
Set<Peer> ret = new HashSet<>();
|
||||
for (NetworkObject.ObjectID peerID : listObjects(Set.of(ObjectStatements.ObjectType.OBJECT_TYPE_PEER)))
|
||||
{
|
||||
Peer peer = (Peer) getObject(peerID);
|
||||
if (peer != getSelfPeer())
|
||||
ret.add(peer);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
|
@ -2,19 +2,20 @@ package moe.nekojimi.friendcloud;
|
|||
|
||||
import moe.nekojimi.friendcloud.objects.NetworkObject;
|
||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||
import moe.nekojimi.friendcloud.storage.Storable;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.*;
|
||||
|
||||
public class ObjectChangeRecord
|
||||
public class ObjectChangeRecord implements Storable
|
||||
{
|
||||
// private final long changeID;
|
||||
|
||||
private final NetworkObject.ObjectID creatorPeer;
|
||||
private final Set<Long> changeHeads = new HashSet<>();
|
||||
private final Set<Change> changes = new HashSet<>();
|
||||
private NetworkObject.ObjectID creatorPeer;
|
||||
private Set<Long> changeHeads = new HashSet<>();
|
||||
private Set<Change> changes = new HashSet<>();
|
||||
|
||||
public ObjectChangeRecord(NetworkObject.ObjectID creatorPeer)
|
||||
{
|
||||
|
@ -69,6 +70,22 @@ public class ObjectChangeRecord
|
|||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getStateMap()
|
||||
{
|
||||
return Map.of("changeHeads", changeHeads,
|
||||
"changes", changes,
|
||||
"creator", creatorPeer.toLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFromStateMap(Map<String, Object> map)
|
||||
{
|
||||
changeHeads = new HashSet<>((Collection) map.get("changeHeads"));
|
||||
changes = new HashSet<>((Collection) map.get("changes"));
|
||||
creatorPeer = new NetworkObject.ObjectID((Long) map.get("creator"));
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
@ -103,19 +120,20 @@ public class ObjectChangeRecord
|
|||
return creatorPeer;
|
||||
}
|
||||
|
||||
public static class Change
|
||||
@Override
|
||||
public long getStorageID()
|
||||
{
|
||||
private final NetworkObject.ObjectID objectID;
|
||||
private final Map<String,String> beforeValues;
|
||||
private final Map<String,String> afterValues;
|
||||
|
||||
public Change(NetworkObject.ObjectID objectID, Map<String, String> before, Map<String, String> after)
|
||||
{
|
||||
this.objectID = objectID;
|
||||
this.beforeValues = before;
|
||||
this.afterValues = after;
|
||||
return getChangeID();
|
||||
}
|
||||
|
||||
public Set<Long> getChangeHeads()
|
||||
{
|
||||
return changeHeads;
|
||||
}
|
||||
|
||||
public record Change(NetworkObject.ObjectID objectID, Map<String, String> beforeValues, Map<String, String> afterValues)
|
||||
{
|
||||
|
||||
public static Change createFromObjectChange(ObjectStatements.ObjectChange change)
|
||||
{
|
||||
return new Change(new NetworkObject.ObjectID(change.getObjectId()), change.getBeforeMap(), change.getAfterMap());
|
||||
|
|
|
@ -37,7 +37,7 @@ public class ObjectChangeTransaction implements Closeable
|
|||
|
||||
public ObjectChangeTransaction addObjectBeforeChange(NetworkObject.ObjectID id)
|
||||
{
|
||||
NetworkObject object = Model.getInstance().getObject(id);
|
||||
NetworkObject object = Main.getInstance().getModel().getObject(id);
|
||||
if (object != null)
|
||||
beforeStates.put(id, object.buildObjectState().build());
|
||||
return this;
|
||||
|
@ -52,7 +52,7 @@ public class ObjectChangeTransaction implements Closeable
|
|||
|
||||
for (Map.Entry<NetworkObject.ObjectID, ObjectStatements.ObjectState> entry : beforeStates.entrySet())
|
||||
{
|
||||
ObjectStatements.ObjectState afterState = Model.getInstance().getObject(entry.getKey()).buildObjectState().build();
|
||||
ObjectStatements.ObjectState afterState = Main.getInstance().getModel().getObject(entry.getKey()).buildObjectState().build();
|
||||
ObjectChangeRecord.Change change = ObjectChangeRecord.Change.createFromObjectStates(entry.getValue(), afterState);
|
||||
changes.add(change);
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ public class ObjectChangeTransaction implements Closeable
|
|||
// end the transaction and get the change object
|
||||
ObjectChangeRecord objectChangeRecord = endTransaction();
|
||||
// add the new change to the model
|
||||
Model.getInstance().addChangeRecord(objectChangeRecord);
|
||||
Main.getInstance().getModel().addChangeRecord(objectChangeRecord);
|
||||
// create a task to propagate the change to other peers
|
||||
Main.getInstance().getExecutor().submit(new PropagateMessageTask(objectChangeRecord.buildObjectChangeMessage().build()));
|
||||
}
|
||||
|
|
|
@ -16,4 +16,5 @@ public class Util
|
|||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package moe.nekojimi.friendcloud.filesystem;
|
|||
|
||||
import jnr.ffi.Pointer;
|
||||
import moe.nekojimi.friendcloud.FileRemoteAccess;
|
||||
import moe.nekojimi.friendcloud.Model;
|
||||
import moe.nekojimi.friendcloud.Main;
|
||||
import moe.nekojimi.friendcloud.objects.NetworkFSNode;
|
||||
import moe.nekojimi.friendcloud.objects.NetworkFile;
|
||||
import moe.nekojimi.friendcloud.objects.NetworkFolder;
|
||||
|
@ -34,7 +34,7 @@ public class FUSEAccess extends FuseStubFS
|
|||
filter.apply(buf, "..", null, 0);
|
||||
// filter.apply(buf,"hello", null, 0);
|
||||
|
||||
for (NetworkFSNode fsNode : Model.getInstance().listFSNodes(path))
|
||||
for (NetworkFSNode fsNode : Main.getInstance().getModel().listFSNodes(path))
|
||||
{
|
||||
filter.apply(buf, fsNode.getName(), null, 0);
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ public class FUSEAccess extends FuseStubFS
|
|||
}
|
||||
else
|
||||
{
|
||||
NetworkFSNode fsNode = Model.getInstance().getFSNode(path);
|
||||
NetworkFSNode fsNode = Main.getInstance().getModel().getFSNode(path);
|
||||
switch (fsNode)
|
||||
{
|
||||
case null ->
|
||||
|
@ -85,7 +85,7 @@ public class FUSEAccess extends FuseStubFS
|
|||
public int open(String path, FuseFileInfo fi)
|
||||
{
|
||||
System.out.println("FUSE: Opening file " + path);
|
||||
NetworkFSNode fsNode = Model.getInstance().getFSNode(path);
|
||||
NetworkFSNode fsNode = Main.getInstance().getModel().getFSNode(path);
|
||||
if (fsNode == null)
|
||||
{
|
||||
System.err.println("FUSE: Failed to open file " + path + ": not found");
|
||||
|
|
|
@ -6,7 +6,7 @@ import com.google.protobuf.ByteString;
|
|||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.google.protobuf.Message;
|
||||
import moe.nekojimi.friendcloud.FilePieceAccess;
|
||||
import moe.nekojimi.friendcloud.Model;
|
||||
import moe.nekojimi.friendcloud.Main;
|
||||
import moe.nekojimi.friendcloud.objects.NetworkFile;
|
||||
import moe.nekojimi.friendcloud.objects.NetworkObject;
|
||||
import moe.nekojimi.friendcloud.objects.Peer;
|
||||
|
@ -86,7 +86,7 @@ public abstract class PeerConnection extends Thread
|
|||
{
|
||||
CommonMessages.MessageHeader.Builder headerBuilder = CommonMessages.MessageHeader.newBuilder()
|
||||
.setMessageId(nextMessageId)
|
||||
.setSenderId(Model.getInstance().getSelfPeer().getObjectID().toLong());
|
||||
.setSenderId(Main.getInstance().getModel().getSelfPeer().getObjectID().toLong());
|
||||
|
||||
if (inReplyTo != null)
|
||||
headerBuilder.setReplyToMessageId(inReplyTo.getMessageId());
|
||||
|
@ -131,7 +131,7 @@ public abstract class PeerConnection extends Thread
|
|||
|
||||
NetworkObject.ObjectID senderID = new NetworkObject.ObjectID(header.getSenderId());
|
||||
if (peer == null)
|
||||
peer = (Peer) Model.getInstance().getOrCreateObject(senderID);
|
||||
peer = (Peer) Main.getInstance().getModel().getOrCreateObject(senderID);
|
||||
else
|
||||
{
|
||||
if (!senderID.equals(peer.getObjectID()))
|
||||
|
@ -187,15 +187,12 @@ public abstract class PeerConnection extends Thread
|
|||
if (body.is(ObjectStatements.ObjectListRequest.class))
|
||||
{
|
||||
ObjectStatements.ObjectListRequest objectListRequest = body.unpack(ObjectStatements.ObjectListRequest.class);
|
||||
List<NetworkObject.ObjectID> objectIDS = Model.getInstance().listObjects(new HashSet<>(objectListRequest.getTypesList()));
|
||||
List<NetworkObject> objects = Main.getInstance().getModel().listObjects(new HashSet<>(objectListRequest.getTypesList()));
|
||||
|
||||
ObjectStatements.ObjectList.Builder objectList = ObjectStatements.ObjectList.newBuilder();
|
||||
for (NetworkObject.ObjectID objectID : objectIDS)
|
||||
for (NetworkObject object : objects)
|
||||
{
|
||||
NetworkObject networkObject = Model.getInstance().getOrCreateObject(objectID);
|
||||
objectList.addStates(networkObject.buildObjectState());
|
||||
// networkObject.updateFromStateMessage();
|
||||
// objectList.addState(networkObject.buildObjectState());
|
||||
objectList.addStates(object.buildObjectState());
|
||||
}
|
||||
System.out.println("Replying to ObjectListRequest with ObjectList, objects=" + objectList.getStatesList());
|
||||
sendMessage(wrapMessage(objectList.build(), header));
|
||||
|
@ -208,7 +205,7 @@ public abstract class PeerConnection extends Thread
|
|||
replyWithError(CommonMessages.Error.ERROR_INVALID_ARGUMENT, header);
|
||||
}
|
||||
|
||||
NetworkFile networkFile = (NetworkFile) Model.getInstance().getObject(new NetworkObject.ObjectID(filePiecesRequestMessage.getFileId()));
|
||||
NetworkFile networkFile = (NetworkFile) Main.getInstance().getModel().getObject(new NetworkObject.ObjectID(filePiecesRequestMessage.getFileId()));
|
||||
if (networkFile == null)
|
||||
{
|
||||
replyWithError(CommonMessages.Error.ERROR_OBJECT_NOT_FOUND, header);
|
||||
|
|
|
@ -2,8 +2,7 @@ package moe.nekojimi.friendcloud.network.requests;
|
|||
|
||||
import com.google.protobuf.Any;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.google.protobuf.Message;
|
||||
import moe.nekojimi.friendcloud.Model;
|
||||
import moe.nekojimi.friendcloud.Main;
|
||||
import moe.nekojimi.friendcloud.ObjectChangeRecord;
|
||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||
|
||||
|
@ -34,7 +33,7 @@ public class ObjectChangeRequest extends Request<ObjectStatements.ObjectChangeRe
|
|||
{
|
||||
ObjectStatements.ObjectChangeMessage objectChangeMessage = reply.unpack(ObjectStatements.ObjectChangeMessage.class);
|
||||
ObjectChangeRecord objectChangeRecord = ObjectChangeRecord.createFromChangeMessage(objectChangeMessage);
|
||||
Model.getInstance().applyChangeRecord(objectChangeRecord);
|
||||
Main.getInstance().getModel().applyChangeRecord(objectChangeRecord);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ package moe.nekojimi.friendcloud.network.requests;
|
|||
|
||||
import com.google.protobuf.Any;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import moe.nekojimi.friendcloud.Model;
|
||||
import moe.nekojimi.friendcloud.Main;
|
||||
import moe.nekojimi.friendcloud.objects.NetworkObject;
|
||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||
|
||||
|
@ -44,7 +44,7 @@ public class ObjectListRequest extends Request<ObjectStatements.ObjectListReques
|
|||
for (ObjectStatements.ObjectState objectState : objectList.getStatesList())
|
||||
{
|
||||
System.out.println("Received state of object " + objectState.getObjectId());
|
||||
NetworkObject object = Model.getInstance().getOrCreateObject(new NetworkObject.ObjectID(objectState.getObjectId()));
|
||||
NetworkObject object = Main.getInstance().getModel().getOrCreateObject(new NetworkObject.ObjectID(objectState.getObjectId()));
|
||||
object.updateFromStateMessage(objectState);
|
||||
ret.add(object);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package moe.nekojimi.friendcloud.objects;
|
||||
|
||||
import moe.nekojimi.friendcloud.Model;
|
||||
import moe.nekojimi.friendcloud.Main;
|
||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class NetworkFSNode extends NetworkObject
|
||||
{
|
||||
// private String path = "";
|
||||
|
@ -24,7 +26,7 @@ public abstract class NetworkFSNode extends NetworkObject
|
|||
{
|
||||
long parentID = Long.parseLong(state.getValuesOrThrow("parent"));
|
||||
if (parentID != 0)
|
||||
parent = (NetworkFolder) Model.getInstance().getObject(new ObjectID(parentID));
|
||||
parent = (NetworkFolder) Main.getInstance().getModel().getObject(new ObjectID(parentID));
|
||||
else
|
||||
parent = null;
|
||||
}
|
||||
|
@ -37,6 +39,22 @@ public abstract class NetworkFSNode extends NetworkObject
|
|||
.putValues("name", getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getStateMap()
|
||||
{
|
||||
Map<String, Object> ret = super.getStateMap();
|
||||
ret.put("name", name);
|
||||
ret.put("parent", parent != null ? parent.getStorageID() : 0L);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFromStateMap(Map<String, Object> map)
|
||||
{
|
||||
name = map.get("name").toString();
|
||||
parent = (NetworkFolder) Main.getInstance().getModel().getObject(new ObjectID(((Number)map.get("parent")).longValue()));
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
|
|
|
@ -9,10 +9,7 @@ import java.nio.file.Files;
|
|||
import java.nio.file.attribute.FileAttribute;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.BitSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HexFormat;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class NetworkFile extends NetworkFSNode
|
||||
|
@ -20,6 +17,7 @@ public class NetworkFile extends NetworkFSNode
|
|||
private static final int MIN_PIECE_SIZE = 0x400; // 1KiB
|
||||
private static final int MAX_PIECE_SIZE = 0x100000; // 1 MiB
|
||||
private static final int IDEAL_PIECE_COUNT = 1024;
|
||||
private static File tempDirectory = null;
|
||||
|
||||
private long size = 0;
|
||||
private long pieceSize = 0;
|
||||
|
@ -31,7 +29,6 @@ public class NetworkFile extends NetworkFSNode
|
|||
private final Map<Peer, PeerFileState> fileStates = new HashMap<>();
|
||||
private BitSet pieces = new BitSet();
|
||||
|
||||
private static File tempDirectory = null;
|
||||
// private List<FilePiece> pieces = new ArrayList<>();
|
||||
|
||||
public NetworkFile(ObjectID objectID)
|
||||
|
@ -118,7 +115,6 @@ public class NetworkFile extends NetworkFSNode
|
|||
hash = HexFormat.of().parseHex(state.getValuesOrThrow("hash"));
|
||||
if (state.containsValues("pieceSize"))
|
||||
pieceSize = Long.parseLong(state.getValuesOrThrow("pieceSize"));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -131,6 +127,34 @@ public class NetworkFile extends NetworkFSNode
|
|||
.putValues("pieceSize", Long.toString(pieceSize));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getStateMap()
|
||||
{
|
||||
Map<String, Object> ret = super.getStateMap();
|
||||
ret.put("size", size);
|
||||
ret.put("hash", HexFormat.of().formatHex(hash));
|
||||
ret.put("pieceSize", pieceSize);
|
||||
ret.put("pieces", Arrays.stream(pieces.toLongArray()).boxed().toList());
|
||||
ret.put("localFile", localFile != null ? localFile.getAbsolutePath() : "");
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFromStateMap(Map<String, Object> map)
|
||||
{
|
||||
super.updateFromStateMap(map);
|
||||
size = ((Number) map.get("size")).longValue();
|
||||
hash = HexFormat.of().parseHex((CharSequence) map.get("hash"));
|
||||
pieceSize = ((Number) map.get("pieceSize")).longValue();
|
||||
ArrayList<Number> pieces1 = (ArrayList<Number>) map.get("pieces");
|
||||
pieces = BitSet.valueOf(pieces1.stream().mapToLong(Number::longValue).toArray());
|
||||
String localFilePath = (String) map.get("localFile");
|
||||
if (localFilePath.isEmpty())
|
||||
localFile = null;
|
||||
else
|
||||
localFile = new File(localFilePath);
|
||||
}
|
||||
|
||||
public File getLocalFile()
|
||||
{
|
||||
return localFile;
|
||||
|
|
|
@ -2,13 +2,13 @@ package moe.nekojimi.friendcloud.objects;
|
|||
|
||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
public class NetworkFolder extends NetworkFSNode
|
||||
{
|
||||
// private final SortedSet<ObjectID> children = new TreeSet<>((a,b)->Long.compare(a.getId(),b.getId()));
|
||||
|
||||
public NetworkFolder(ObjectID objectID)
|
||||
{
|
||||
|
@ -18,8 +18,18 @@ public class NetworkFolder extends NetworkFSNode
|
|||
@Override
|
||||
public ObjectStatements.ObjectState.Builder buildObjectState()
|
||||
{
|
||||
return super.buildObjectState().putValues("name", name);
|
||||
return super.buildObjectState();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getStateMap()
|
||||
{
|
||||
return super.getStateMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFromStateMap(Map<String, Object> map)
|
||||
{
|
||||
super.updateFromStateMap(map);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
package moe.nekojimi.friendcloud.objects;
|
||||
|
||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||
import moe.nekojimi.friendcloud.storage.Storable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class NetworkObject
|
||||
public abstract class NetworkObject implements Storable
|
||||
{
|
||||
private final ObjectID objectID;
|
||||
|
||||
|
@ -18,6 +21,21 @@ public abstract class NetworkObject
|
|||
return objectID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStorageID()
|
||||
{
|
||||
return getObjectID().toLong();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getStateMap()
|
||||
{
|
||||
Map<String, Object> ret = new HashMap<>();
|
||||
ret.put("id", objectID.toLong());
|
||||
return ret;
|
||||
}
|
||||
|
||||
public synchronized void updateFromStateMessage(ObjectStatements.ObjectState state)
|
||||
{
|
||||
if (state.getObjectId() != objectID.toLong())
|
||||
|
|
|
@ -4,10 +4,7 @@ import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
|||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Peer extends NetworkObject
|
||||
|
@ -73,6 +70,25 @@ public class Peer extends NetworkObject
|
|||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getStateMap()
|
||||
{
|
||||
Map<String, Object> ret = super.getStateMap();
|
||||
ret.put("userName", userName);
|
||||
ret.put("systemName", systemName);
|
||||
ret.put("addresses", addresses);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFromStateMap(Map<String, Object> map)
|
||||
{
|
||||
userName = map.get("userName").toString();
|
||||
systemName = map.get("systemName").toString();
|
||||
addresses.clear();
|
||||
addresses.addAll((Collection<? extends URI>) map.get("addresses"));
|
||||
}
|
||||
|
||||
public void addAddress(URI address)
|
||||
{
|
||||
addresses.add(address);
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package moe.nekojimi.friendcloud.objects;
|
||||
|
||||
import moe.nekojimi.friendcloud.Model;
|
||||
import moe.nekojimi.friendcloud.Main;
|
||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class PeerFileState extends NetworkObject
|
||||
{
|
||||
private Peer peer;
|
||||
|
@ -19,8 +21,8 @@ public class PeerFileState extends NetworkObject
|
|||
public void updateFromStateMessage(ObjectStatements.ObjectState state)
|
||||
{
|
||||
super.updateFromStateMessage(state);
|
||||
peer = (Peer) Model.getInstance().getOrCreateObject(new ObjectID(Long.parseLong(state.getValuesOrThrow("peer"))));
|
||||
file = (NetworkFile) Model.getInstance().getOrCreateObject(new ObjectID(Long.parseLong(state.getValuesOrThrow("file"))));
|
||||
peer = (Peer) Main.getInstance().getModel().getOrCreateObject(new ObjectID(Long.parseLong(state.getValuesOrThrow("peer"))));
|
||||
file = (NetworkFile) Main.getInstance().getModel().getOrCreateObject(new ObjectID(Long.parseLong(state.getValuesOrThrow("file"))));
|
||||
if (state.containsValues("progress"))
|
||||
progress = Double.parseDouble(state.getValuesOrThrow("progress"));
|
||||
|
||||
|
@ -72,4 +74,10 @@ public class PeerFileState extends NetworkObject
|
|||
{
|
||||
return peer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFromStateMap(Map<String, Object> map)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
package moe.nekojimi.friendcloud.storage;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
public class CachingDataStore extends DataStore
|
||||
{
|
||||
private final DataStore backend;
|
||||
|
||||
public CachingDataStore(DataStore backend)
|
||||
{
|
||||
this.backend = backend;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized <T extends Storable> DAO<T> getDAOForClass(Class<T> clazz)
|
||||
{
|
||||
if (daos.containsKey(clazz))
|
||||
return (DAO<T>) daos.get(clazz);
|
||||
else
|
||||
{
|
||||
CachingDAO<T> ret = new CachingDAO<>(clazz);
|
||||
daos.put(clazz, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FSNodeDAO getFSDAO()
|
||||
{
|
||||
return backend.getFSDAO();
|
||||
}
|
||||
|
||||
private Map<Class<? extends Storable>, CachingDAO<?>> daos = new HashMap<>();
|
||||
|
||||
public class CachingDAO<T extends Storable> implements DAO<T>
|
||||
{
|
||||
private final DAO<T> backendDao;
|
||||
private WeakHashMap<Long, T> cache = new WeakHashMap<>();
|
||||
|
||||
public CachingDAO(Class<T> clazz)
|
||||
{
|
||||
this.backendDao = backend.getDAOForClass(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized List<T> getAll()
|
||||
{
|
||||
List<T> ret = new ArrayList<>();
|
||||
ret.addAll(cache.values());
|
||||
for (T t : backendDao.getAll())
|
||||
{
|
||||
if (!cache.containsKey(t.getStorageID()))
|
||||
{
|
||||
ret.add(t);
|
||||
cache.put(t.getStorageID(), t);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(long id)
|
||||
{
|
||||
return backendDao.exists(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T create(long id)
|
||||
{
|
||||
T t = backendDao.create(id);
|
||||
cache.put(id, t);
|
||||
return t;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(long id)
|
||||
{
|
||||
T t = backendDao.get(id);
|
||||
cache.put(id, t);
|
||||
return t;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(T object)
|
||||
{
|
||||
long id = object.getStorageID();
|
||||
backendDao.update(object);
|
||||
cache.put(id, object);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package moe.nekojimi.friendcloud.storage;
|
||||
|
||||
import moe.nekojimi.friendcloud.objects.NetworkFSNode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class DataStore
|
||||
{
|
||||
public abstract <T extends Storable> DAO<T> getDAOForClass(Class<T> clazz);
|
||||
public abstract FSNodeDAO getFSDAO();
|
||||
|
||||
public interface DAO<T extends Storable>
|
||||
{
|
||||
List<T> getAll();
|
||||
boolean exists(long id);
|
||||
T create(long id);
|
||||
T get(long id);
|
||||
default T getOrCreate(long id)
|
||||
{
|
||||
T old = get(id);
|
||||
if (old != null)
|
||||
return old;
|
||||
return create(id);
|
||||
}
|
||||
void update(T object);
|
||||
}
|
||||
|
||||
public interface FSNodeDAO extends DAO<NetworkFSNode>
|
||||
{
|
||||
default List<NetworkFSNode> getPathChildren(String path)
|
||||
{
|
||||
System.err.println("WARNING: using violently cursed implementation of getPathChildren, make a better one now!!!");
|
||||
return getAll().stream().filter(networkFSNode -> networkFSNode.getNetworkPath().startsWith(path)).toList();
|
||||
}
|
||||
}
|
||||
|
||||
public class ClassFusionDAO<T extends Storable> implements DAO<T>
|
||||
{
|
||||
private final Set<Class<? extends T>> subclasses;
|
||||
|
||||
@SafeVarargs
|
||||
public ClassFusionDAO(Class<? extends T>... subclasses)
|
||||
{
|
||||
this.subclasses = Set.of(subclasses);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> getAll()
|
||||
{
|
||||
List<T> ret = new ArrayList<>();
|
||||
for (Class<? extends T> subclass : subclasses)
|
||||
ret.addAll(getDAOForClass(subclass).getAll());
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(long id)
|
||||
{
|
||||
for (Class<? extends T> subclass : subclasses)
|
||||
if (getDAOForClass(subclass).exists(id))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T create(long id)
|
||||
{
|
||||
throw new UnsupportedOperationException("That type's abstract, can't create it");
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(long id)
|
||||
{
|
||||
for (Class<? extends T> subclass : subclasses)
|
||||
if (getDAOForClass(subclass).exists(id))
|
||||
return getDAOForClass(subclass).get(id);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(T object)
|
||||
{
|
||||
throw new UnsupportedOperationException("That type's abstract, can't update it");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package moe.nekojimi.friendcloud.storage;
|
||||
|
||||
import moe.nekojimi.friendcloud.Main;
|
||||
import moe.nekojimi.friendcloud.objects.NetworkObject;
|
||||
import moe.nekojimi.friendcloud.objects.Peer;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class LocalData implements Storable
|
||||
{
|
||||
private Peer localPeer;
|
||||
|
||||
public Peer getLocalPeer()
|
||||
{
|
||||
return localPeer;
|
||||
}
|
||||
|
||||
public void setLocalPeer(Peer localPeer)
|
||||
{
|
||||
this.localPeer = localPeer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getStorageID()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getStateMap()
|
||||
{
|
||||
return Map.of("localPeer", localPeer.getObjectID().toLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFromStateMap(Map<String, Object> map)
|
||||
{
|
||||
localPeer = (Peer) Main.getInstance().getModel().getObject(new NetworkObject.ObjectID((Long) map.get("localPeer")));
|
||||
}
|
||||
}
|
184
src/main/java/moe/nekojimi/friendcloud/storage/Model.java
Normal file
184
src/main/java/moe/nekojimi/friendcloud/storage/Model.java
Normal file
|
@ -0,0 +1,184 @@
|
|||
package moe.nekojimi.friendcloud.storage;
|
||||
|
||||
import moe.nekojimi.friendcloud.ObjectChangeRecord;
|
||||
import moe.nekojimi.friendcloud.objects.*;
|
||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Model
|
||||
{
|
||||
|
||||
private final CachingDataStore dataStore;
|
||||
|
||||
private final int systemID;
|
||||
|
||||
private Peer selfPeer = null;
|
||||
private ObjectChangeRecord currentChange;
|
||||
|
||||
public Model(DataStore dataStore)
|
||||
{
|
||||
this.dataStore = new CachingDataStore(dataStore);
|
||||
Random ran = new Random();
|
||||
systemID = ran.nextInt() & 0x00FFFFFF;
|
||||
}
|
||||
|
||||
public void setSelfPeer(Peer selfPeer)
|
||||
{
|
||||
this.selfPeer = selfPeer;
|
||||
}
|
||||
|
||||
public synchronized Peer getSelfPeer()
|
||||
{
|
||||
if (selfPeer == null)
|
||||
selfPeer = createObjectByType(ObjectStatements.ObjectType.OBJECT_TYPE_PEER);
|
||||
return selfPeer;
|
||||
}
|
||||
// private Map<Long, Node> nodes = new HashMap<>();
|
||||
|
||||
public synchronized NetworkObject.ObjectID getNextObjectID(ObjectStatements.ObjectType type)
|
||||
{
|
||||
Random ran = new Random();
|
||||
int randomNumber = ran.nextInt();
|
||||
NetworkObject.ObjectID objectID = new NetworkObject.ObjectID(type, systemID, randomNumber);
|
||||
System.out.println("Assigned new object ID: " + objectID);
|
||||
return objectID;
|
||||
}
|
||||
|
||||
public static Class<? extends NetworkObject> getNetworkObjectClassByType(ObjectStatements.ObjectType type)
|
||||
{
|
||||
return switch (type)
|
||||
{
|
||||
case OBJECT_TYPE_FILE -> NetworkFile.class;
|
||||
case OBJECT_TYPE_FOLDER -> NetworkFolder.class;
|
||||
case OBJECT_TYPE_PEER -> Peer.class;
|
||||
case OBJECT_TYPE_PEER_FILE_STATE -> PeerFileState.class;
|
||||
case OBJECT_TYPE_UNSPECIFIED, UNRECOGNIZED -> throw new IllegalArgumentException("???");
|
||||
default -> throw new UnsupportedOperationException("NYI");
|
||||
};
|
||||
}
|
||||
|
||||
public synchronized <T extends NetworkObject> T createObjectByID(NetworkObject.ObjectID id)
|
||||
{
|
||||
if (id.toLong() == 0)
|
||||
throw new IllegalArgumentException("Cannot create an object with ID=0!");
|
||||
ObjectStatements.ObjectType type = id.getType();
|
||||
System.out.println("Creating new object with type: " + type.name());
|
||||
if (type == ObjectStatements.ObjectType.OBJECT_TYPE_UNSPECIFIED)
|
||||
throw new IllegalArgumentException();
|
||||
T ret = (T) dataStore.getDAOForClass(getNetworkObjectClassByType(type)).create(id.toLong());
|
||||
return ret;
|
||||
}
|
||||
|
||||
public synchronized <T extends NetworkObject> T createObjectByType(ObjectStatements.ObjectType type)
|
||||
{
|
||||
return createObjectByID(getNextObjectID(type));
|
||||
}
|
||||
|
||||
public synchronized <T extends NetworkObject> T getObject(NetworkObject.ObjectID id)
|
||||
{
|
||||
if (id.toLong() == 0)
|
||||
return null;
|
||||
Class<T> clazz = (Class<T>) getNetworkObjectClassByType(id.getType());
|
||||
return dataStore.getDAOForClass(clazz).get(id.toLong());
|
||||
}
|
||||
|
||||
public synchronized <T extends NetworkObject> T getOrCreateObject(NetworkObject.ObjectID id)
|
||||
{
|
||||
if (id.toLong() == 0)
|
||||
return null;
|
||||
Class<T> clazz = (Class<T>) getNetworkObjectClassByType(id.getType());
|
||||
return dataStore.getDAOForClass(clazz).getOrCreate(id.toLong());
|
||||
}
|
||||
|
||||
public synchronized List<NetworkObject> listObjects(Set<ObjectStatements.ObjectType> types)
|
||||
{
|
||||
List<NetworkObject> ret = new ArrayList<>();
|
||||
Set<Class<? extends NetworkObject>> classes = types.stream().map(Model::getNetworkObjectClassByType).collect(Collectors.toSet());
|
||||
for (Class<? extends NetworkObject> clazz: classes)
|
||||
{
|
||||
List<? extends NetworkObject> list = dataStore.getDAOForClass(clazz).getAll();
|
||||
ret.addAll(list);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public synchronized List<NetworkFSNode> listFSNodes(String path)
|
||||
{
|
||||
//TODO: dumbest algorithm in the world
|
||||
|
||||
List<NetworkFSNode> ret = new ArrayList<>();
|
||||
for (NetworkObject object : listObjects(Set.of(ObjectStatements.ObjectType.OBJECT_TYPE_FILE, ObjectStatements.ObjectType.OBJECT_TYPE_FOLDER)))
|
||||
{
|
||||
NetworkFSNode fsNode = (NetworkFSNode) object;
|
||||
String networkPath = fsNode.getNetworkPath();
|
||||
if (networkPath.substring(0, networkPath.lastIndexOf("/")+1).equals(path))
|
||||
ret.add(fsNode);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public synchronized NetworkFSNode getFSNode(String path)
|
||||
{
|
||||
for (NetworkObject object : listObjects(Set.of(ObjectStatements.ObjectType.OBJECT_TYPE_FILE, ObjectStatements.ObjectType.OBJECT_TYPE_FOLDER)))
|
||||
{
|
||||
NetworkFSNode fsNode = (NetworkFSNode) object;
|
||||
String networkPath = fsNode.getNetworkPath();
|
||||
if (networkPath.equals(path))
|
||||
return fsNode;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public synchronized void addChangeRecord(ObjectChangeRecord record)
|
||||
{
|
||||
dataStore.getDAOForClass(ObjectChangeRecord.class).update(record);
|
||||
}
|
||||
|
||||
public ObjectChangeRecord getChangeRecord(long id)
|
||||
{
|
||||
return dataStore.getDAOForClass(ObjectChangeRecord.class).get(id);
|
||||
}
|
||||
|
||||
public void applyChangeRecord(ObjectChangeRecord record)
|
||||
{
|
||||
if (!record.getChangeHeads().contains(currentChange.getChangeID()))
|
||||
throw new IllegalStateException("Change does not apply! Valid change heads=" + record.getChangeHeads() + ", we are in state " + currentChange.getChangeID());
|
||||
addChangeRecord(record);
|
||||
|
||||
|
||||
// if (record == null)
|
||||
// throw new IllegalArgumentException("Cannot apply unknown change!");
|
||||
}
|
||||
|
||||
public Set<ObjectChangeRecord> getChangeHeads()
|
||||
{
|
||||
// stupid algorithm - start with all of the changes, then remove the ones that are referenced by something
|
||||
// TODO: better algorithm
|
||||
Set<ObjectChangeRecord> ret = new HashSet<>(dataStore.getDAOForClass(ObjectChangeRecord.class).getAll());
|
||||
// for (ObjectChangeRecord record : changeRecords.values())
|
||||
// {
|
||||
// throw new UnsupportedOperationException("NYI");
|
||||
// }
|
||||
throw new UnsupportedOperationException("NYI");
|
||||
}
|
||||
|
||||
public Set<Peer> listOtherPeers()
|
||||
{
|
||||
Set<Peer> ret = new HashSet<>();
|
||||
for (NetworkObject object : listObjects(Set.of(ObjectStatements.ObjectType.OBJECT_TYPE_PEER)))
|
||||
{
|
||||
Peer peer = (Peer) object;
|
||||
if (peer != getSelfPeer())
|
||||
ret.add(peer);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public <T extends Storable> void objectChanged(T storable)
|
||||
{
|
||||
Class<T> clazz = (Class<T>) storable.getClass();
|
||||
dataStore.getDAOForClass(clazz).update(storable);
|
||||
}
|
||||
}
|
29
src/main/java/moe/nekojimi/friendcloud/storage/Storable.java
Normal file
29
src/main/java/moe/nekojimi/friendcloud/storage/Storable.java
Normal file
|
@ -0,0 +1,29 @@
|
|||
package moe.nekojimi.friendcloud.storage;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface Storable
|
||||
{
|
||||
/**
|
||||
* Gets the unique ID used for persisting the object on disk. Must remain constant for an object's lifetime.
|
||||
* @return the unique ID.
|
||||
*/
|
||||
long getStorageID();
|
||||
|
||||
// /**
|
||||
// * Gets a constant string specifying the namespace that the ID numbers returned by getStorageID() are unique within. Defaults to the class name.
|
||||
// * @return the namespace name.
|
||||
// */
|
||||
// default String getStorageNamespace()
|
||||
// {
|
||||
// return this.getClass().getName().toLowerCase();
|
||||
// }
|
||||
|
||||
/**
|
||||
* Gets a map representing all serializable (i.e. to be saved) fields of this object, by name.
|
||||
* Fields may be any of the following types: String, Integer, Long, Double, Collection\<X\>, Boolean, Map\<String, X\>
|
||||
*/
|
||||
Map<String, Object> getStateMap();
|
||||
|
||||
void updateFromStateMap(Map<String, Object> map);
|
||||
}
|
|
@ -0,0 +1,334 @@
|
|||
package moe.nekojimi.friendcloud.storage;
|
||||
|
||||
import moe.nekojimi.friendcloud.objects.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
|
||||
public class StupidJSONFileStore extends DataStore
|
||||
{
|
||||
private final File storageDirectory;
|
||||
|
||||
private final Map<Class<? extends Storable>, DAO<? extends Storable>> daos = new HashMap<>();
|
||||
|
||||
public StupidJSONFileStore(File storageDirectory)
|
||||
{
|
||||
this.storageDirectory = storageDirectory;
|
||||
if (!storageDirectory.exists())
|
||||
{
|
||||
if (!storageDirectory.mkdirs())
|
||||
throw new RuntimeException("Unable to create storage directory " + storageDirectory.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Storable> DAO<T> getDAOForClass(Class<T> clazz)
|
||||
{
|
||||
if (daos.containsKey(clazz))
|
||||
return (DAO<T>) daos.get(clazz);
|
||||
|
||||
DAO<T> ret;
|
||||
if (clazz.equals(NetworkFile.class))
|
||||
ret = (DAO<T>) new NetworkFileDAO();
|
||||
else if (clazz.equals(NetworkFolder.class))
|
||||
ret = (DAO<T>) new NetworkFolderDAO();
|
||||
else if (clazz.equals(Peer.class))
|
||||
ret = (DAO<T>) new PeerDAO();
|
||||
else if (clazz.equals(PeerFileState.class))
|
||||
ret = (DAO<T>) new PeerFileStateDAO();
|
||||
else if (clazz.equals(NetworkFSNode.class))
|
||||
ret = (DAO<T>) new NetworkFSNodeDAO();
|
||||
else
|
||||
throw new UnsupportedOperationException("Requested DAO for unsupported type " + clazz.getCanonicalName());
|
||||
|
||||
daos.put(clazz, ret);
|
||||
return (DAO<T>) ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FSNodeDAO getFSDAO()
|
||||
{
|
||||
return new NetworkFSNodeDAO();
|
||||
}
|
||||
|
||||
private abstract class JSONObjectDAO<T extends Storable> implements DAO<T>
|
||||
{
|
||||
protected final Set<@NotNull Class<?>> VALID_JSON_CLASSES = Set.of(new Class[]{Boolean.class, Double.class, Integer.class, Long.class, String.class, JSONArray.class, JSONObject.class, JSONObject.NULL.getClass()});
|
||||
|
||||
protected abstract String getNamespace();
|
||||
protected File getNamespaceDirectory()
|
||||
{
|
||||
File ret = new File(storageDirectory, getNamespace());
|
||||
if (!ret.exists())
|
||||
ret.mkdir();
|
||||
return ret;
|
||||
}
|
||||
protected abstract T makeBlank(long id);
|
||||
protected T jsonToObject(JSONObject json)
|
||||
{
|
||||
long id = json.getLong("storageID");
|
||||
T ret = makeBlank(id);
|
||||
ret.updateFromStateMap(jsonToMap(json));
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected JSONObject objectToJson(T object)
|
||||
{
|
||||
Map<String, Object> stateMap = object.getStateMap();
|
||||
return mapToJson(stateMap).put("storageID", object.getStorageID());
|
||||
}
|
||||
|
||||
private JSONObject mapToJson(Map<String, Object> stateMap)
|
||||
{
|
||||
JSONObject ret = new JSONObject();
|
||||
for (Map.Entry<String, Object> e : stateMap.entrySet())
|
||||
{
|
||||
Class<?> valueClass = e.getValue().getClass();
|
||||
if (VALID_JSON_CLASSES.contains(valueClass))
|
||||
ret.put(e.getKey(), e.getValue());
|
||||
else if (Collection.class.isAssignableFrom(valueClass))
|
||||
{
|
||||
Collection<?> valueCollection = (Collection<?>) e.getValue();
|
||||
List<Object> writeList = new ArrayList<>(valueCollection.size());
|
||||
for (Object item: valueCollection)
|
||||
if (VALID_JSON_CLASSES.contains(item.getClass()))
|
||||
writeList.add(item);
|
||||
else
|
||||
writeList.add(serialiseWeirdObject(item));
|
||||
ret.put(e.getKey(), writeList);
|
||||
}
|
||||
else if (Map.class.isAssignableFrom(valueClass))
|
||||
{
|
||||
Map<String, Object> valueMap = (Map<String, Object>) e.getValue();
|
||||
return mapToJson(valueMap);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.put(e.getKey(), serialiseWeirdObject(e.getValue()));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
private Map<String,Object> jsonToMap(JSONObject json)
|
||||
{
|
||||
Map<String, Object> ret = new HashMap<>();
|
||||
for (String key : json.keySet())
|
||||
{
|
||||
ret.put(key,deserialiseSomething(json.get(key)));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected Object deserialiseSomething(Object value)
|
||||
{
|
||||
Object ret = value;
|
||||
if (value instanceof JSONObject)
|
||||
{
|
||||
JSONObject valueJsonObject = (JSONObject) value;
|
||||
if (valueJsonObject.has("weirdObjectClass"))
|
||||
ret = deserialiseWeirdObject(valueJsonObject);
|
||||
else
|
||||
ret = jsonToMap(valueJsonObject);
|
||||
}
|
||||
else if (value instanceof JSONArray)
|
||||
{
|
||||
JSONArray valueJsonArray = (JSONArray) value;
|
||||
List<Object> readList = new ArrayList<Object>(valueJsonArray.length());
|
||||
for (int i = 0; i < valueJsonArray.length(); i++)
|
||||
{
|
||||
Object item = deserialiseSomething(valueJsonArray.get(i));
|
||||
readList.add(item);
|
||||
}
|
||||
return readList;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected JSONObject serialiseWeirdObject(Object value) throws IllegalArgumentException
|
||||
{
|
||||
throw new IllegalArgumentException("Don't know how to serialise a " + value.getClass().getCanonicalName() );
|
||||
}
|
||||
protected Object deserialiseWeirdObject(JSONObject json)
|
||||
{
|
||||
throw new IllegalArgumentException("Don't know how to deserialise that.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(long id)
|
||||
{
|
||||
File file = new File(getNamespaceDirectory(), Long.toHexString(id) + ".json");
|
||||
return file.exists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> getAll()
|
||||
{
|
||||
List<T> ret = new ArrayList<>();
|
||||
|
||||
// get all files in the storage directory
|
||||
for (File file : Objects.requireNonNull(getNamespaceDirectory().listFiles()))
|
||||
{
|
||||
try
|
||||
{
|
||||
JSONObject json = new JSONObject(Files.readString(file.toPath()));
|
||||
ret.add(jsonToObject(json));
|
||||
} catch (IOException e)
|
||||
{
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T create(long id)
|
||||
{
|
||||
T ret = makeBlank(id);
|
||||
update(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(long id)
|
||||
{
|
||||
File file = new File(getNamespaceDirectory(), Long.toHexString(id) + ".json");
|
||||
try
|
||||
{
|
||||
JSONObject json = new JSONObject(Files.readString(file.toPath()));
|
||||
return jsonToObject(json);
|
||||
} catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(T object)
|
||||
{
|
||||
File file = new File(getNamespaceDirectory(), Long.toHexString(object.getStorageID()) + ".json");
|
||||
try(FileWriter writer = new FileWriter(file, false))
|
||||
{
|
||||
objectToJson(object).write(writer);
|
||||
writer.flush();
|
||||
} catch (IOException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class NetworkObjectDAO<T extends NetworkObject> extends JSONObjectDAO<T>
|
||||
{
|
||||
@Override
|
||||
protected String getNamespace()
|
||||
{
|
||||
return "networkObjects";
|
||||
}
|
||||
}
|
||||
|
||||
private class NetworkFSNodeDAO extends ClassFusionDAO<NetworkFSNode> implements FSNodeDAO
|
||||
{
|
||||
public NetworkFSNodeDAO()
|
||||
{
|
||||
super(NetworkFile.class, NetworkFolder.class);
|
||||
}
|
||||
}
|
||||
|
||||
private class NetworkFileDAO extends NetworkObjectDAO<NetworkFile>
|
||||
{
|
||||
@Override
|
||||
protected String getNamespace()
|
||||
{
|
||||
return super.getNamespace() + "/files";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NetworkFile makeBlank(long id)
|
||||
{
|
||||
return new NetworkFile(new NetworkObject.ObjectID(id));
|
||||
}
|
||||
}
|
||||
|
||||
private class NetworkFolderDAO extends NetworkObjectDAO<NetworkFolder>
|
||||
{
|
||||
@Override
|
||||
protected String getNamespace()
|
||||
{
|
||||
return super.getNamespace() + "/folders";
|
||||
}
|
||||
@Override
|
||||
protected NetworkFolder makeBlank(long id)
|
||||
{
|
||||
return new NetworkFolder(new NetworkObject.ObjectID(id));
|
||||
}
|
||||
}
|
||||
|
||||
private class PeerDAO extends NetworkObjectDAO<Peer>
|
||||
{
|
||||
@Override
|
||||
protected String getNamespace()
|
||||
{
|
||||
return super.getNamespace() + "/peers";
|
||||
}
|
||||
@Override
|
||||
protected Peer makeBlank(long id)
|
||||
{
|
||||
return new Peer(new NetworkObject.ObjectID(id));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JSONObject serialiseWeirdObject(Object value) throws IllegalArgumentException
|
||||
{
|
||||
if (value instanceof URI)
|
||||
{
|
||||
URI uri = (URI) value;
|
||||
return new JSONObject().put("weirdObjectClass", URI.class.getCanonicalName()).put("uri",uri.toString());
|
||||
}
|
||||
return super.serialiseWeirdObject(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object deserialiseWeirdObject(JSONObject json)
|
||||
{
|
||||
String weirdType = json.getString("weirdObjectClass");
|
||||
try
|
||||
{
|
||||
Class<?> weirdClass = Class.forName(weirdType);
|
||||
if (weirdClass == URI.class)
|
||||
return new URI(json.getString("uri"));
|
||||
} catch (ClassNotFoundException e)
|
||||
{
|
||||
throw new IllegalArgumentException(e);
|
||||
} catch (URISyntaxException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return super.deserialiseWeirdObject(json);
|
||||
}
|
||||
}
|
||||
|
||||
private class PeerFileStateDAO extends NetworkObjectDAO<PeerFileState>
|
||||
{
|
||||
@Override
|
||||
protected String getNamespace()
|
||||
{
|
||||
return super.getNamespace() + "/peerFileStates";
|
||||
}
|
||||
@Override
|
||||
protected PeerFileState makeBlank(long id)
|
||||
{
|
||||
return new PeerFileState(new NetworkObject.ObjectID(id));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
package moe.nekojimi.friendcloud.tasks;
|
||||
|
||||
import moe.nekojimi.friendcloud.Main;
|
||||
import moe.nekojimi.friendcloud.Model;
|
||||
import moe.nekojimi.friendcloud.ObjectChangeTransaction;
|
||||
import moe.nekojimi.friendcloud.objects.NetworkObject;
|
||||
import moe.nekojimi.friendcloud.objects.Peer;
|
||||
|
@ -19,11 +18,11 @@ public class JoinNetworkTask implements Runnable
|
|||
NetworkObject.ObjectID peerID = null;
|
||||
try (ObjectChangeTransaction builder = ObjectChangeTransaction.startTransaction(Main.getInstance().getConnectionManager(), peerID))
|
||||
{
|
||||
Peer selfPeer = Model.getInstance().getSelfPeer();
|
||||
Peer selfPeer = Main.getInstance().getModel().getSelfPeer();
|
||||
if (selfPeer != null)
|
||||
peerID = selfPeer.getObjectID();
|
||||
else
|
||||
peerID = Model.getInstance().getNextObjectID(ObjectStatements.ObjectType.OBJECT_TYPE_PEER);
|
||||
peerID = Main.getInstance().getModel().getNextObjectID(ObjectStatements.ObjectType.OBJECT_TYPE_PEER);
|
||||
|
||||
// synchronise with the network
|
||||
SyncWithNetworkTask syncWithNetworkTask = new SyncWithNetworkTask();
|
||||
|
|
|
@ -3,10 +3,8 @@ package moe.nekojimi.friendcloud.tasks;
|
|||
import com.google.protobuf.Message;
|
||||
import moe.nekojimi.friendcloud.ConnectionManager;
|
||||
import moe.nekojimi.friendcloud.Main;
|
||||
import moe.nekojimi.friendcloud.Model;
|
||||
import moe.nekojimi.friendcloud.network.PeerConnection;
|
||||
import moe.nekojimi.friendcloud.objects.Peer;
|
||||
import moe.nekojimi.friendcloud.protos.CommonMessages;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -23,7 +21,7 @@ public class PropagateMessageTask implements Runnable
|
|||
public void run()
|
||||
{
|
||||
ConnectionManager connectionManager = Main.getInstance().getConnectionManager();
|
||||
for (Peer peer: Model.getInstance().listOtherPeers())
|
||||
for (Peer peer: Main.getInstance().getModel().listOtherPeers())
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package moe.nekojimi.friendcloud.tasks;
|
||||
|
||||
import moe.nekojimi.friendcloud.Main;
|
||||
import moe.nekojimi.friendcloud.Model;
|
||||
import moe.nekojimi.friendcloud.ObjectChangeRecord;
|
||||
import moe.nekojimi.friendcloud.network.PeerConnection;
|
||||
import moe.nekojimi.friendcloud.network.requests.ObjectChangeRequest;
|
||||
|
@ -20,14 +19,14 @@ public class SyncWithNetworkTask implements Runnable
|
|||
public void run()
|
||||
{
|
||||
// for each other peer:
|
||||
for (Peer peer : Model.getInstance().listOtherPeers())
|
||||
for (Peer peer : Main.getInstance().getModel().listOtherPeers())
|
||||
{
|
||||
// open a connection
|
||||
try
|
||||
{
|
||||
PeerConnection connection = Main.getInstance().getConnectionManager().getNodeConnection(peer);
|
||||
// send a ObjectChangeRequest
|
||||
ObjectChangeRequest objectChangeRequest = new ObjectChangeRequest(Model.getInstance().getChangeHeads().stream().map(ObjectChangeRecord::getChangeID).collect(Collectors.toSet()));
|
||||
ObjectChangeRequest objectChangeRequest = new ObjectChangeRequest(Main.getInstance().getModel().getChangeHeads().stream().map(ObjectChangeRecord::getChangeID).collect(Collectors.toSet()));
|
||||
CompletableFuture<Set<ObjectStatements.ObjectChange>> future = connection.makeRequest(objectChangeRequest);
|
||||
|
||||
// integrate the returned changes with our change graph
|
||||
|
|
Loading…
Reference in a new issue