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>
|
<artifactId>gethostname4j</artifactId>
|
||||||
<version>1.0.0</version>
|
<version>1.0.0</version>
|
||||||
</dependency>
|
</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>
|
</dependencies>
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,12 @@ import com.offbynull.portmapper.gateways.process.ProcessGateway;
|
||||||
import com.offbynull.portmapper.mapper.MappedPort;
|
import com.offbynull.portmapper.mapper.MappedPort;
|
||||||
import com.offbynull.portmapper.mapper.PortMapper;
|
import com.offbynull.portmapper.mapper.PortMapper;
|
||||||
import com.offbynull.portmapper.mapper.PortType;
|
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 jnr.ffi.Platform;
|
||||||
import moe.nekojimi.friendcloud.filesystem.FUSEAccess;
|
import moe.nekojimi.friendcloud.filesystem.FUSEAccess;
|
||||||
import moe.nekojimi.friendcloud.network.PeerConnection;
|
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.NetworkFile;
|
||||||
import moe.nekojimi.friendcloud.objects.NetworkObject;
|
import moe.nekojimi.friendcloud.objects.NetworkObject;
|
||||||
import moe.nekojimi.friendcloud.objects.Peer;
|
import moe.nekojimi.friendcloud.objects.Peer;
|
||||||
import moe.nekojimi.friendcloud.objects.PeerFileState;
|
|
||||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
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 org.slf4j.simple.SimpleLogger;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -28,6 +36,7 @@ import java.net.*;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
@ -37,7 +46,7 @@ public class Main
|
||||||
private static Main instance;
|
private static Main instance;
|
||||||
|
|
||||||
@Parameter(names="-share")
|
@Parameter(names="-share")
|
||||||
private List<String> sharedFiles = new ArrayList<>();
|
private List<String> sharedFilePaths = new ArrayList<>();
|
||||||
|
|
||||||
@Parameter(names="-known-peer")
|
@Parameter(names="-known-peer")
|
||||||
private List<String> knownPeers = new ArrayList<>();
|
private List<String> knownPeers = new ArrayList<>();
|
||||||
|
@ -48,13 +57,15 @@ public class Main
|
||||||
@Parameter(names="-no-upnp")
|
@Parameter(names="-no-upnp")
|
||||||
private boolean noUpnp = false;
|
private boolean noUpnp = false;
|
||||||
|
|
||||||
|
@Parameter(names="-create-network")
|
||||||
|
private boolean createNetwork = false;
|
||||||
|
|
||||||
// @Parameter(names="-file")
|
// @Parameter(names="-file")
|
||||||
|
|
||||||
private ConnectionManager connectionManager;
|
private ConnectionManager connectionManager;
|
||||||
|
|
||||||
private final ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(16);
|
private final ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(16);
|
||||||
|
|
||||||
private final FUSEAccess fuseAccess = new FUSEAccess();
|
private final FUSEAccess fuseAccess = new FUSEAccess();
|
||||||
|
private final Model model = new Model(new StupidJSONFileStore(new File("storage")));
|
||||||
|
|
||||||
public static void main(String[] args)
|
public static void main(String[] args)
|
||||||
{
|
{
|
||||||
|
@ -66,7 +77,7 @@ public class Main
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
instance.run();
|
instance.run();
|
||||||
} catch (IOException | InterruptedException e)
|
} catch (IOException | InterruptedException | JLibnotifyLoadException | JLibnotifyInitException e)
|
||||||
{
|
{
|
||||||
e.printStackTrace(System.err);
|
e.printStackTrace(System.err);
|
||||||
try
|
try
|
||||||
|
@ -81,7 +92,7 @@ public class Main
|
||||||
// TestMessage.SearchRequest request = TestMessage.SearchRequest.newBuilder().setQuery("bees!").setPageNumber(316).setResultsPerPage(42069).build();
|
// 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);
|
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();
|
connectionManager.start();
|
||||||
|
|
||||||
String hostname = Hostname.getHostname();
|
String hostname = Hostname.getHostname();
|
||||||
Model.getInstance().getSelfPeer().setSystemName(hostname);
|
model.getSelfPeer().setSystemName(hostname);
|
||||||
Model.getInstance().getSelfPeer().setUserName(System.getProperty("user.name") + "-" + tcpPort);
|
model.getSelfPeer().setUserName(System.getProperty("user.name") + "-" + tcpPort);
|
||||||
addHostAddress(InetAddress.getLocalHost());
|
addHostAddress(InetAddress.getLocalHost());
|
||||||
|
model.objectChanged(model.getSelfPeer());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Startup procedure:
|
Startup procedure:
|
||||||
|
@ -133,23 +150,43 @@ public class Main
|
||||||
if (!noUpnp)
|
if (!noUpnp)
|
||||||
setupIGP();
|
setupIGP();
|
||||||
|
|
||||||
for (String sharedFilePath: sharedFiles)
|
Set<File> sharedFiles = new HashSet<>();
|
||||||
|
for (String sharedFilePath: sharedFilePaths)
|
||||||
|
{
|
||||||
|
sharedFiles.add(new File(sharedFilePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<NetworkObject> knownFiles = model.listObjects(Set.of(ObjectStatements.ObjectType.OBJECT_TYPE_FILE));
|
||||||
|
|
||||||
|
for (NetworkObject knownFile: knownFiles)
|
||||||
|
{
|
||||||
|
NetworkFile f = (NetworkFile) knownFile;
|
||||||
|
boolean removed = sharedFiles.remove(f.getLocalFile());
|
||||||
|
if (removed)
|
||||||
|
System.out.println("Identified known local file " + f.getObjectID() + " = " + f.getLocalFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (File sharedFile: sharedFiles)
|
||||||
{
|
{
|
||||||
File file = new File(sharedFilePath);
|
if (sharedFile.exists())
|
||||||
if (file.exists())
|
|
||||||
{
|
{
|
||||||
System.out.println("Adding shared network file: " + file.getAbsolutePath());
|
System.out.println("Adding shared network file: " + sharedFile.getAbsolutePath());
|
||||||
|
|
||||||
NetworkFile networkFile = (NetworkFile) Model.getInstance().createObjectByType(ObjectStatements.ObjectType.OBJECT_TYPE_FILE);
|
NetworkFile networkFile = (NetworkFile) model.createObjectByType(ObjectStatements.ObjectType.OBJECT_TYPE_FILE);
|
||||||
networkFile.updateFromLocalFile(file);
|
networkFile.updateFromLocalFile(sharedFile);
|
||||||
|
model.objectChanged(networkFile);
|
||||||
|
|
||||||
PeerFileState peerFileState = (PeerFileState) Model.getInstance().createObjectByType(ObjectStatements.ObjectType.OBJECT_TYPE_PEER_FILE_STATE);
|
// PeerFileState peerFileState = (PeerFileState) model.createObjectByType(ObjectStatements.ObjectType.OBJECT_TYPE_PEER_FILE_STATE);
|
||||||
peerFileState.setNode(Model.getInstance().getSelfPeer());
|
// peerFileState.setNode(model.getSelfPeer());
|
||||||
peerFileState.setFile(networkFile);
|
// peerFileState.setFile(networkFile);
|
||||||
peerFileState.setProgress(100);
|
// peerFileState.setProgress(100);
|
||||||
|
// model.objectChanged(peerFileState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JoinNetworkTask joinNetworkTask = new JoinNetworkTask();
|
||||||
|
executor.submit(joinNetworkTask);
|
||||||
|
|
||||||
for (String knownPeerAddress : knownPeers)
|
for (String knownPeerAddress : knownPeers)
|
||||||
{
|
{
|
||||||
String[] split = knownPeerAddress.split(":");
|
String[] split = knownPeerAddress.split(":");
|
||||||
|
@ -165,21 +202,7 @@ public class Main
|
||||||
URI uri = new URI("tcp", null, address.getHostString(), address.getPort(), null, null, null);
|
URI uri = new URI("tcp", null, address.getHostString(), address.getPort(), null, null, null);
|
||||||
PeerConnection nodeConnection = connectionManager.getNodeConnection(uri);
|
PeerConnection nodeConnection = connectionManager.getNodeConnection(uri);
|
||||||
|
|
||||||
resquestCompleteState(nodeConnection);
|
requestCompleteState(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);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
|
|
||||||
} catch (ConnectException ex)
|
} catch (ConnectException ex)
|
||||||
{
|
{
|
||||||
System.out.println("Couldn't connect to host " + address);
|
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(
|
CompletableFuture<List<NetworkObject>> objectListFuture = nodeConnection.makeRequest(new ObjectListRequest(Set.of(
|
||||||
ObjectStatements.ObjectType.OBJECT_TYPE_FILE,
|
ObjectStatements.ObjectType.OBJECT_TYPE_FILE,
|
||||||
|
@ -202,7 +225,7 @@ public class Main
|
||||||
private void addHostAddress(InetAddress address)
|
private void addHostAddress(InetAddress address)
|
||||||
{
|
{
|
||||||
String host = address.getCanonicalHostName();
|
String host = address.getCanonicalHostName();
|
||||||
Peer selfNode = Model.getInstance().getSelfPeer();
|
Peer selfNode = model.getSelfPeer();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
URI uri = new URI("tcp", null, host, tcpPort, null, null, null);
|
URI uri = new URI("tcp", null, host, tcpPort, null, null, null);
|
||||||
|
@ -281,4 +304,9 @@ public class Main
|
||||||
{
|
{
|
||||||
return connectionManager;
|
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.objects.NetworkObject;
|
||||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||||
|
import moe.nekojimi.friendcloud.storage.Storable;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class ObjectChangeRecord
|
public class ObjectChangeRecord implements Storable
|
||||||
{
|
{
|
||||||
// private final long changeID;
|
// private final long changeID;
|
||||||
|
|
||||||
private final NetworkObject.ObjectID creatorPeer;
|
private NetworkObject.ObjectID creatorPeer;
|
||||||
private final Set<Long> changeHeads = new HashSet<>();
|
private Set<Long> changeHeads = new HashSet<>();
|
||||||
private final Set<Change> changes = new HashSet<>();
|
private Set<Change> changes = new HashSet<>();
|
||||||
|
|
||||||
public ObjectChangeRecord(NetworkObject.ObjectID creatorPeer)
|
public ObjectChangeRecord(NetworkObject.ObjectID creatorPeer)
|
||||||
{
|
{
|
||||||
|
@ -69,6 +70,22 @@ public class ObjectChangeRecord
|
||||||
return builder;
|
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()
|
public String toString()
|
||||||
{
|
{
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
@ -103,68 +120,69 @@ public class ObjectChangeRecord
|
||||||
return creatorPeer;
|
return creatorPeer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Change
|
@Override
|
||||||
|
public long getStorageID()
|
||||||
{
|
{
|
||||||
private final NetworkObject.ObjectID objectID;
|
return getChangeID();
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Change createFromObjectChange(ObjectStatements.ObjectChange change)
|
|
||||||
{
|
|
||||||
return new Change(new NetworkObject.ObjectID(change.getObjectId()), change.getBeforeMap(), change.getAfterMap());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Change createFromObjectStates(ObjectStatements.ObjectState before, ObjectStatements.ObjectState after)
|
|
||||||
{
|
|
||||||
Map<String,String> beforeValues = new HashMap<>();
|
|
||||||
Map<String,String> afterValues = new HashMap<>();
|
|
||||||
for (String key: after.getValuesMap().keySet())
|
|
||||||
{
|
|
||||||
String beforeValue = before.getValuesOrDefault(key, null);
|
|
||||||
String afterValue = after.getValuesOrDefault(key, null);
|
|
||||||
if (!afterValue.equals(beforeValue))
|
|
||||||
{
|
|
||||||
beforeValues.put(key,beforeValue);
|
|
||||||
afterValues.put(key,afterValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!afterValues.isEmpty())
|
|
||||||
{
|
|
||||||
return new Change(new NetworkObject.ObjectID(before.getObjectId()), beforeValues, afterValues);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append(objectID.toLong()).append(";"); // The object ID, then ;
|
|
||||||
// now all key-value pairs in alphabetical order
|
|
||||||
List<String> keys = new ArrayList<>(beforeValues.keySet());
|
|
||||||
Collections.sort(keys);
|
|
||||||
for (String key : keys)
|
|
||||||
{
|
|
||||||
sb.append(key).append(":").append(afterValues.get(key));
|
|
||||||
}
|
|
||||||
sb.append(";");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public ObjectStatements.ObjectChange.Builder buildObjectChange()
|
|
||||||
{
|
|
||||||
ObjectStatements.ObjectChange.Builder builder = ObjectStatements.ObjectChange.newBuilder();
|
|
||||||
builder.putAllBefore(beforeValues);
|
|
||||||
builder.putAllAfter(afterValues);
|
|
||||||
builder.setObjectId(objectID.toLong());
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Change createFromObjectStates(ObjectStatements.ObjectState before, ObjectStatements.ObjectState after)
|
||||||
|
{
|
||||||
|
Map<String, String> beforeValues = new HashMap<>();
|
||||||
|
Map<String, String> afterValues = new HashMap<>();
|
||||||
|
for (String key : after.getValuesMap().keySet())
|
||||||
|
{
|
||||||
|
String beforeValue = before.getValuesOrDefault(key, null);
|
||||||
|
String afterValue = after.getValuesOrDefault(key, null);
|
||||||
|
if (!afterValue.equals(beforeValue))
|
||||||
|
{
|
||||||
|
beforeValues.put(key, beforeValue);
|
||||||
|
afterValues.put(key, afterValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!afterValues.isEmpty())
|
||||||
|
{
|
||||||
|
return new Change(new NetworkObject.ObjectID(before.getObjectId()), beforeValues, afterValues);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(objectID.toLong()).append(";"); // The object ID, then ;
|
||||||
|
// now all key-value pairs in alphabetical order
|
||||||
|
List<String> keys = new ArrayList<>(beforeValues.keySet());
|
||||||
|
Collections.sort(keys);
|
||||||
|
for (String key : keys)
|
||||||
|
{
|
||||||
|
sb.append(key).append(":").append(afterValues.get(key));
|
||||||
|
}
|
||||||
|
sb.append(";");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ObjectStatements.ObjectChange.Builder buildObjectChange()
|
||||||
|
{
|
||||||
|
ObjectStatements.ObjectChange.Builder builder = ObjectStatements.ObjectChange.newBuilder();
|
||||||
|
builder.putAllBefore(beforeValues);
|
||||||
|
builder.putAllAfter(afterValues);
|
||||||
|
builder.setObjectId(objectID.toLong());
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ public class ObjectChangeTransaction implements Closeable
|
||||||
|
|
||||||
public ObjectChangeTransaction addObjectBeforeChange(NetworkObject.ObjectID id)
|
public ObjectChangeTransaction addObjectBeforeChange(NetworkObject.ObjectID id)
|
||||||
{
|
{
|
||||||
NetworkObject object = Model.getInstance().getObject(id);
|
NetworkObject object = Main.getInstance().getModel().getObject(id);
|
||||||
if (object != null)
|
if (object != null)
|
||||||
beforeStates.put(id, object.buildObjectState().build());
|
beforeStates.put(id, object.buildObjectState().build());
|
||||||
return this;
|
return this;
|
||||||
|
@ -52,7 +52,7 @@ public class ObjectChangeTransaction implements Closeable
|
||||||
|
|
||||||
for (Map.Entry<NetworkObject.ObjectID, ObjectStatements.ObjectState> entry : beforeStates.entrySet())
|
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);
|
ObjectChangeRecord.Change change = ObjectChangeRecord.Change.createFromObjectStates(entry.getValue(), afterState);
|
||||||
changes.add(change);
|
changes.add(change);
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ public class ObjectChangeTransaction implements Closeable
|
||||||
// end the transaction and get the change object
|
// end the transaction and get the change object
|
||||||
ObjectChangeRecord objectChangeRecord = endTransaction();
|
ObjectChangeRecord objectChangeRecord = endTransaction();
|
||||||
// add the new change to the model
|
// 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
|
// create a task to propagate the change to other peers
|
||||||
Main.getInstance().getExecutor().submit(new PropagateMessageTask(objectChangeRecord.buildObjectChangeMessage().build()));
|
Main.getInstance().getExecutor().submit(new PropagateMessageTask(objectChangeRecord.buildObjectChangeMessage().build()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,4 +16,5 @@ public class Util
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package moe.nekojimi.friendcloud.filesystem;
|
||||||
|
|
||||||
import jnr.ffi.Pointer;
|
import jnr.ffi.Pointer;
|
||||||
import moe.nekojimi.friendcloud.FileRemoteAccess;
|
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.NetworkFSNode;
|
||||||
import moe.nekojimi.friendcloud.objects.NetworkFile;
|
import moe.nekojimi.friendcloud.objects.NetworkFile;
|
||||||
import moe.nekojimi.friendcloud.objects.NetworkFolder;
|
import moe.nekojimi.friendcloud.objects.NetworkFolder;
|
||||||
|
@ -34,7 +34,7 @@ public class FUSEAccess extends FuseStubFS
|
||||||
filter.apply(buf, "..", null, 0);
|
filter.apply(buf, "..", null, 0);
|
||||||
// filter.apply(buf,"hello", 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);
|
filter.apply(buf, fsNode.getName(), null, 0);
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ public class FUSEAccess extends FuseStubFS
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
NetworkFSNode fsNode = Model.getInstance().getFSNode(path);
|
NetworkFSNode fsNode = Main.getInstance().getModel().getFSNode(path);
|
||||||
switch (fsNode)
|
switch (fsNode)
|
||||||
{
|
{
|
||||||
case null ->
|
case null ->
|
||||||
|
@ -85,7 +85,7 @@ public class FUSEAccess extends FuseStubFS
|
||||||
public int open(String path, FuseFileInfo fi)
|
public int open(String path, FuseFileInfo fi)
|
||||||
{
|
{
|
||||||
System.out.println("FUSE: Opening file " + path);
|
System.out.println("FUSE: Opening file " + path);
|
||||||
NetworkFSNode fsNode = Model.getInstance().getFSNode(path);
|
NetworkFSNode fsNode = Main.getInstance().getModel().getFSNode(path);
|
||||||
if (fsNode == null)
|
if (fsNode == null)
|
||||||
{
|
{
|
||||||
System.err.println("FUSE: Failed to open file " + path + ": not found");
|
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.InvalidProtocolBufferException;
|
||||||
import com.google.protobuf.Message;
|
import com.google.protobuf.Message;
|
||||||
import moe.nekojimi.friendcloud.FilePieceAccess;
|
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.NetworkFile;
|
||||||
import moe.nekojimi.friendcloud.objects.NetworkObject;
|
import moe.nekojimi.friendcloud.objects.NetworkObject;
|
||||||
import moe.nekojimi.friendcloud.objects.Peer;
|
import moe.nekojimi.friendcloud.objects.Peer;
|
||||||
|
@ -86,7 +86,7 @@ public abstract class PeerConnection extends Thread
|
||||||
{
|
{
|
||||||
CommonMessages.MessageHeader.Builder headerBuilder = CommonMessages.MessageHeader.newBuilder()
|
CommonMessages.MessageHeader.Builder headerBuilder = CommonMessages.MessageHeader.newBuilder()
|
||||||
.setMessageId(nextMessageId)
|
.setMessageId(nextMessageId)
|
||||||
.setSenderId(Model.getInstance().getSelfPeer().getObjectID().toLong());
|
.setSenderId(Main.getInstance().getModel().getSelfPeer().getObjectID().toLong());
|
||||||
|
|
||||||
if (inReplyTo != null)
|
if (inReplyTo != null)
|
||||||
headerBuilder.setReplyToMessageId(inReplyTo.getMessageId());
|
headerBuilder.setReplyToMessageId(inReplyTo.getMessageId());
|
||||||
|
@ -131,7 +131,7 @@ public abstract class PeerConnection extends Thread
|
||||||
|
|
||||||
NetworkObject.ObjectID senderID = new NetworkObject.ObjectID(header.getSenderId());
|
NetworkObject.ObjectID senderID = new NetworkObject.ObjectID(header.getSenderId());
|
||||||
if (peer == null)
|
if (peer == null)
|
||||||
peer = (Peer) Model.getInstance().getOrCreateObject(senderID);
|
peer = (Peer) Main.getInstance().getModel().getOrCreateObject(senderID);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!senderID.equals(peer.getObjectID()))
|
if (!senderID.equals(peer.getObjectID()))
|
||||||
|
@ -187,15 +187,12 @@ public abstract class PeerConnection extends Thread
|
||||||
if (body.is(ObjectStatements.ObjectListRequest.class))
|
if (body.is(ObjectStatements.ObjectListRequest.class))
|
||||||
{
|
{
|
||||||
ObjectStatements.ObjectListRequest objectListRequest = body.unpack(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();
|
ObjectStatements.ObjectList.Builder objectList = ObjectStatements.ObjectList.newBuilder();
|
||||||
for (NetworkObject.ObjectID objectID : objectIDS)
|
for (NetworkObject object : objects)
|
||||||
{
|
{
|
||||||
NetworkObject networkObject = Model.getInstance().getOrCreateObject(objectID);
|
objectList.addStates(object.buildObjectState());
|
||||||
objectList.addStates(networkObject.buildObjectState());
|
|
||||||
// networkObject.updateFromStateMessage();
|
|
||||||
// objectList.addState(networkObject.buildObjectState());
|
|
||||||
}
|
}
|
||||||
System.out.println("Replying to ObjectListRequest with ObjectList, objects=" + objectList.getStatesList());
|
System.out.println("Replying to ObjectListRequest with ObjectList, objects=" + objectList.getStatesList());
|
||||||
sendMessage(wrapMessage(objectList.build(), header));
|
sendMessage(wrapMessage(objectList.build(), header));
|
||||||
|
@ -208,7 +205,7 @@ public abstract class PeerConnection extends Thread
|
||||||
replyWithError(CommonMessages.Error.ERROR_INVALID_ARGUMENT, header);
|
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)
|
if (networkFile == null)
|
||||||
{
|
{
|
||||||
replyWithError(CommonMessages.Error.ERROR_OBJECT_NOT_FOUND, header);
|
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.Any;
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
import com.google.protobuf.Message;
|
import moe.nekojimi.friendcloud.Main;
|
||||||
import moe.nekojimi.friendcloud.Model;
|
|
||||||
import moe.nekojimi.friendcloud.ObjectChangeRecord;
|
import moe.nekojimi.friendcloud.ObjectChangeRecord;
|
||||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
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);
|
ObjectStatements.ObjectChangeMessage objectChangeMessage = reply.unpack(ObjectStatements.ObjectChangeMessage.class);
|
||||||
ObjectChangeRecord objectChangeRecord = ObjectChangeRecord.createFromChangeMessage(objectChangeMessage);
|
ObjectChangeRecord objectChangeRecord = ObjectChangeRecord.createFromChangeMessage(objectChangeMessage);
|
||||||
Model.getInstance().applyChangeRecord(objectChangeRecord);
|
Main.getInstance().getModel().applyChangeRecord(objectChangeRecord);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ package moe.nekojimi.friendcloud.network.requests;
|
||||||
|
|
||||||
import com.google.protobuf.Any;
|
import com.google.protobuf.Any;
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
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.objects.NetworkObject;
|
||||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class ObjectListRequest extends Request<ObjectStatements.ObjectListRequest, List<NetworkObject>>
|
public class ObjectListRequest extends Request<ObjectStatements.ObjectListRequest, List<NetworkObject>>
|
||||||
{
|
{
|
||||||
private final Set<ObjectStatements.ObjectType> types;
|
private final Set<ObjectStatements.ObjectType> types;
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ public class ObjectListRequest extends Request<ObjectStatements.ObjectListReques
|
||||||
for (ObjectStatements.ObjectState objectState : objectList.getStatesList())
|
for (ObjectStatements.ObjectState objectState : objectList.getStatesList())
|
||||||
{
|
{
|
||||||
System.out.println("Received state of object " + objectState.getObjectId());
|
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);
|
object.updateFromStateMessage(objectState);
|
||||||
ret.add(object);
|
ret.add(object);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package moe.nekojimi.friendcloud.objects;
|
package moe.nekojimi.friendcloud.objects;
|
||||||
|
|
||||||
import moe.nekojimi.friendcloud.Model;
|
import moe.nekojimi.friendcloud.Main;
|
||||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public abstract class NetworkFSNode extends NetworkObject
|
public abstract class NetworkFSNode extends NetworkObject
|
||||||
{
|
{
|
||||||
// private String path = "";
|
// private String path = "";
|
||||||
|
@ -24,7 +26,7 @@ public abstract class NetworkFSNode extends NetworkObject
|
||||||
{
|
{
|
||||||
long parentID = Long.parseLong(state.getValuesOrThrow("parent"));
|
long parentID = Long.parseLong(state.getValuesOrThrow("parent"));
|
||||||
if (parentID != 0)
|
if (parentID != 0)
|
||||||
parent = (NetworkFolder) Model.getInstance().getObject(new ObjectID(parentID));
|
parent = (NetworkFolder) Main.getInstance().getModel().getObject(new ObjectID(parentID));
|
||||||
else
|
else
|
||||||
parent = null;
|
parent = null;
|
||||||
}
|
}
|
||||||
|
@ -37,6 +39,22 @@ public abstract class NetworkFSNode extends NetworkObject
|
||||||
.putValues("name", getName());
|
.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()
|
public String getName()
|
||||||
{
|
{
|
||||||
return name;
|
return name;
|
||||||
|
|
|
@ -9,10 +9,7 @@ import java.nio.file.Files;
|
||||||
import java.nio.file.attribute.FileAttribute;
|
import java.nio.file.attribute.FileAttribute;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.BitSet;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HexFormat;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class NetworkFile extends NetworkFSNode
|
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 MIN_PIECE_SIZE = 0x400; // 1KiB
|
||||||
private static final int MAX_PIECE_SIZE = 0x100000; // 1 MiB
|
private static final int MAX_PIECE_SIZE = 0x100000; // 1 MiB
|
||||||
private static final int IDEAL_PIECE_COUNT = 1024;
|
private static final int IDEAL_PIECE_COUNT = 1024;
|
||||||
|
private static File tempDirectory = null;
|
||||||
|
|
||||||
private long size = 0;
|
private long size = 0;
|
||||||
private long pieceSize = 0;
|
private long pieceSize = 0;
|
||||||
|
@ -31,7 +29,6 @@ public class NetworkFile extends NetworkFSNode
|
||||||
private final Map<Peer, PeerFileState> fileStates = new HashMap<>();
|
private final Map<Peer, PeerFileState> fileStates = new HashMap<>();
|
||||||
private BitSet pieces = new BitSet();
|
private BitSet pieces = new BitSet();
|
||||||
|
|
||||||
private static File tempDirectory = null;
|
|
||||||
// private List<FilePiece> pieces = new ArrayList<>();
|
// private List<FilePiece> pieces = new ArrayList<>();
|
||||||
|
|
||||||
public NetworkFile(ObjectID objectID)
|
public NetworkFile(ObjectID objectID)
|
||||||
|
@ -118,7 +115,6 @@ public class NetworkFile extends NetworkFSNode
|
||||||
hash = HexFormat.of().parseHex(state.getValuesOrThrow("hash"));
|
hash = HexFormat.of().parseHex(state.getValuesOrThrow("hash"));
|
||||||
if (state.containsValues("pieceSize"))
|
if (state.containsValues("pieceSize"))
|
||||||
pieceSize = Long.parseLong(state.getValuesOrThrow("pieceSize"));
|
pieceSize = Long.parseLong(state.getValuesOrThrow("pieceSize"));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -131,6 +127,34 @@ public class NetworkFile extends NetworkFSNode
|
||||||
.putValues("pieceSize", Long.toString(pieceSize));
|
.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()
|
public File getLocalFile()
|
||||||
{
|
{
|
||||||
return localFile;
|
return localFile;
|
||||||
|
|
|
@ -2,13 +2,13 @@ package moe.nekojimi.friendcloud.objects;
|
||||||
|
|
||||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
public class NetworkFolder extends NetworkFSNode
|
public class NetworkFolder extends NetworkFSNode
|
||||||
{
|
{
|
||||||
// private final SortedSet<ObjectID> children = new TreeSet<>((a,b)->Long.compare(a.getId(),b.getId()));
|
|
||||||
|
|
||||||
public NetworkFolder(ObjectID objectID)
|
public NetworkFolder(ObjectID objectID)
|
||||||
{
|
{
|
||||||
|
@ -18,8 +18,18 @@ public class NetworkFolder extends NetworkFSNode
|
||||||
@Override
|
@Override
|
||||||
public ObjectStatements.ObjectState.Builder buildObjectState()
|
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;
|
package moe.nekojimi.friendcloud.objects;
|
||||||
|
|
||||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||||
|
import moe.nekojimi.friendcloud.storage.Storable;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public abstract class NetworkObject
|
public abstract class NetworkObject implements Storable
|
||||||
{
|
{
|
||||||
private final ObjectID objectID;
|
private final ObjectID objectID;
|
||||||
|
|
||||||
|
@ -18,6 +21,21 @@ public abstract class NetworkObject
|
||||||
return objectID;
|
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)
|
public synchronized void updateFromStateMessage(ObjectStatements.ObjectState state)
|
||||||
{
|
{
|
||||||
if (state.getObjectId() != objectID.toLong())
|
if (state.getObjectId() != objectID.toLong())
|
||||||
|
|
|
@ -4,10 +4,7 @@ import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class Peer extends NetworkObject
|
public class Peer extends NetworkObject
|
||||||
|
@ -73,7 +70,26 @@ public class Peer extends NetworkObject
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addAddress(URI address)
|
@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);
|
addresses.add(address);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package moe.nekojimi.friendcloud.objects;
|
package moe.nekojimi.friendcloud.objects;
|
||||||
|
|
||||||
import moe.nekojimi.friendcloud.Model;
|
import moe.nekojimi.friendcloud.Main;
|
||||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class PeerFileState extends NetworkObject
|
public class PeerFileState extends NetworkObject
|
||||||
{
|
{
|
||||||
private Peer peer;
|
private Peer peer;
|
||||||
|
@ -19,8 +21,8 @@ public class PeerFileState extends NetworkObject
|
||||||
public void updateFromStateMessage(ObjectStatements.ObjectState state)
|
public void updateFromStateMessage(ObjectStatements.ObjectState state)
|
||||||
{
|
{
|
||||||
super.updateFromStateMessage(state);
|
super.updateFromStateMessage(state);
|
||||||
peer = (Peer) Model.getInstance().getOrCreateObject(new ObjectID(Long.parseLong(state.getValuesOrThrow("peer"))));
|
peer = (Peer) Main.getInstance().getModel().getOrCreateObject(new ObjectID(Long.parseLong(state.getValuesOrThrow("peer"))));
|
||||||
file = (NetworkFile) Model.getInstance().getOrCreateObject(new ObjectID(Long.parseLong(state.getValuesOrThrow("file"))));
|
file = (NetworkFile) Main.getInstance().getModel().getOrCreateObject(new ObjectID(Long.parseLong(state.getValuesOrThrow("file"))));
|
||||||
if (state.containsValues("progress"))
|
if (state.containsValues("progress"))
|
||||||
progress = Double.parseDouble(state.getValuesOrThrow("progress"));
|
progress = Double.parseDouble(state.getValuesOrThrow("progress"));
|
||||||
|
|
||||||
|
@ -72,4 +74,10 @@ public class PeerFileState extends NetworkObject
|
||||||
{
|
{
|
||||||
return peer;
|
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;
|
package moe.nekojimi.friendcloud.tasks;
|
||||||
|
|
||||||
import moe.nekojimi.friendcloud.Main;
|
import moe.nekojimi.friendcloud.Main;
|
||||||
import moe.nekojimi.friendcloud.Model;
|
|
||||||
import moe.nekojimi.friendcloud.ObjectChangeTransaction;
|
import moe.nekojimi.friendcloud.ObjectChangeTransaction;
|
||||||
import moe.nekojimi.friendcloud.objects.NetworkObject;
|
import moe.nekojimi.friendcloud.objects.NetworkObject;
|
||||||
import moe.nekojimi.friendcloud.objects.Peer;
|
import moe.nekojimi.friendcloud.objects.Peer;
|
||||||
|
@ -19,11 +18,11 @@ public class JoinNetworkTask implements Runnable
|
||||||
NetworkObject.ObjectID peerID = null;
|
NetworkObject.ObjectID peerID = null;
|
||||||
try (ObjectChangeTransaction builder = ObjectChangeTransaction.startTransaction(Main.getInstance().getConnectionManager(), peerID))
|
try (ObjectChangeTransaction builder = ObjectChangeTransaction.startTransaction(Main.getInstance().getConnectionManager(), peerID))
|
||||||
{
|
{
|
||||||
Peer selfPeer = Model.getInstance().getSelfPeer();
|
Peer selfPeer = Main.getInstance().getModel().getSelfPeer();
|
||||||
if (selfPeer != null)
|
if (selfPeer != null)
|
||||||
peerID = selfPeer.getObjectID();
|
peerID = selfPeer.getObjectID();
|
||||||
else
|
else
|
||||||
peerID = Model.getInstance().getNextObjectID(ObjectStatements.ObjectType.OBJECT_TYPE_PEER);
|
peerID = Main.getInstance().getModel().getNextObjectID(ObjectStatements.ObjectType.OBJECT_TYPE_PEER);
|
||||||
|
|
||||||
// synchronise with the network
|
// synchronise with the network
|
||||||
SyncWithNetworkTask syncWithNetworkTask = new SyncWithNetworkTask();
|
SyncWithNetworkTask syncWithNetworkTask = new SyncWithNetworkTask();
|
||||||
|
|
|
@ -3,10 +3,8 @@ package moe.nekojimi.friendcloud.tasks;
|
||||||
import com.google.protobuf.Message;
|
import com.google.protobuf.Message;
|
||||||
import moe.nekojimi.friendcloud.ConnectionManager;
|
import moe.nekojimi.friendcloud.ConnectionManager;
|
||||||
import moe.nekojimi.friendcloud.Main;
|
import moe.nekojimi.friendcloud.Main;
|
||||||
import moe.nekojimi.friendcloud.Model;
|
|
||||||
import moe.nekojimi.friendcloud.network.PeerConnection;
|
import moe.nekojimi.friendcloud.network.PeerConnection;
|
||||||
import moe.nekojimi.friendcloud.objects.Peer;
|
import moe.nekojimi.friendcloud.objects.Peer;
|
||||||
import moe.nekojimi.friendcloud.protos.CommonMessages;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@ -23,7 +21,7 @@ public class PropagateMessageTask implements Runnable
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
ConnectionManager connectionManager = Main.getInstance().getConnectionManager();
|
ConnectionManager connectionManager = Main.getInstance().getConnectionManager();
|
||||||
for (Peer peer: Model.getInstance().listOtherPeers())
|
for (Peer peer: Main.getInstance().getModel().listOtherPeers())
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package moe.nekojimi.friendcloud.tasks;
|
package moe.nekojimi.friendcloud.tasks;
|
||||||
|
|
||||||
import moe.nekojimi.friendcloud.Main;
|
import moe.nekojimi.friendcloud.Main;
|
||||||
import moe.nekojimi.friendcloud.Model;
|
|
||||||
import moe.nekojimi.friendcloud.ObjectChangeRecord;
|
import moe.nekojimi.friendcloud.ObjectChangeRecord;
|
||||||
import moe.nekojimi.friendcloud.network.PeerConnection;
|
import moe.nekojimi.friendcloud.network.PeerConnection;
|
||||||
import moe.nekojimi.friendcloud.network.requests.ObjectChangeRequest;
|
import moe.nekojimi.friendcloud.network.requests.ObjectChangeRequest;
|
||||||
|
@ -20,14 +19,14 @@ public class SyncWithNetworkTask implements Runnable
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
// for each other peer:
|
// for each other peer:
|
||||||
for (Peer peer : Model.getInstance().listOtherPeers())
|
for (Peer peer : Main.getInstance().getModel().listOtherPeers())
|
||||||
{
|
{
|
||||||
// open a connection
|
// open a connection
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
PeerConnection connection = Main.getInstance().getConnectionManager().getNodeConnection(peer);
|
PeerConnection connection = Main.getInstance().getConnectionManager().getNodeConnection(peer);
|
||||||
// send a ObjectChangeRequest
|
// 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);
|
CompletableFuture<Set<ObjectStatements.ObjectChange>> future = connection.makeRequest(objectChangeRequest);
|
||||||
|
|
||||||
// integrate the returned changes with our change graph
|
// integrate the returned changes with our change graph
|
||||||
|
|
Loading…
Reference in a new issue