Fixed some stuff
This commit is contained in:
parent
5b7c6a857a
commit
855bca957f
34
pom.xml
34
pom.xml
|
@ -74,24 +74,24 @@
|
||||||
<artifactId>jlibnotify</artifactId>
|
<artifactId>jlibnotify</artifactId>
|
||||||
<version>1.1.0</version>
|
<version>1.1.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<!-- <dependency>-->
|
||||||
<groupId>com.github.hypfvieh</groupId>
|
<!-- <groupId>com.github.hypfvieh</groupId>-->
|
||||||
<artifactId>dbus-java-core</artifactId>
|
<!-- <artifactId>dbus-java-core</artifactId>-->
|
||||||
<version>5.1.0-SNAPSHOT</version>
|
<!-- <version>5.1.0-SNAPSHOT</version>-->
|
||||||
</dependency>
|
<!-- </dependency>-->
|
||||||
<!-- Unixsocket support using native unix socket implementation of Java (since Java 16) -->
|
<!-- <!– Unixsocket support using native unix socket implementation of Java (since Java 16) –>-->
|
||||||
<dependency>
|
<!-- <dependency>-->
|
||||||
<groupId>com.github.hypfvieh</groupId>
|
<!-- <groupId>com.github.hypfvieh</groupId>-->
|
||||||
<artifactId>dbus-java-transport-native-unixsocket</artifactId>
|
<!-- <artifactId>dbus-java-transport-native-unixsocket</artifactId>-->
|
||||||
<version>5.1.0-SNAPSHOT</version>
|
<!-- <version>5.1.0-SNAPSHOT</version>-->
|
||||||
</dependency>
|
<!-- </dependency>-->
|
||||||
|
|
||||||
<!-- Add this if you want support for TCP based DBus connections -->
|
<!-- <!– Add this if you want support for TCP based DBus connections –>-->
|
||||||
<dependency>
|
<!-- <dependency>-->
|
||||||
<groupId>com.github.hypfvieh</groupId>
|
<!-- <groupId>com.github.hypfvieh</groupId>-->
|
||||||
<artifactId>dbus-java-transport-tcp</artifactId>
|
<!-- <artifactId>dbus-java-transport-tcp</artifactId>-->
|
||||||
<version>5.1.0-SNAPSHOT</version>
|
<!-- <version>5.1.0-SNAPSHOT</version>-->
|
||||||
</dependency>
|
<!-- </dependency>-->
|
||||||
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
|
@ -48,12 +48,13 @@ public class ConnectionManager extends Thread
|
||||||
{
|
{
|
||||||
consumer.accept(nodeTCPConnection);
|
consumer.accept(nodeTCPConnection);
|
||||||
}
|
}
|
||||||
} catch (IOException e)
|
} catch (Exception e)
|
||||||
{
|
{
|
||||||
System.err.println("ConnectionManager TCP experienced exception:" + e.getMessage());
|
System.err.println("ConnectionManager experienced exception:" + e.getMessage());
|
||||||
e.printStackTrace(System.err);
|
e.printStackTrace(System.err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
System.err.println("ConnectionManager: thread dying!");
|
||||||
}
|
}
|
||||||
|
|
||||||
public PeerConnection getNodeConnection(URI uri) throws IOException
|
public PeerConnection getNodeConnection(URI uri) throws IOException
|
||||||
|
@ -81,13 +82,14 @@ public class ConnectionManager extends Thread
|
||||||
return nodeConnection;
|
return nodeConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PeerConnection getNodeConnection(Peer peer) throws IOException
|
public PeerConnection getNodeConnection(Peer peer)
|
||||||
{
|
{
|
||||||
// try to find if we already have an active connection to this peer
|
// try to find if we already have an active connection to this peer
|
||||||
purgeDeadConnections();
|
purgeDeadConnections();
|
||||||
|
System.out.println("ConnectionManager: trying to get connection to " + peer + " (have " + activeConnections.size() + " connections open)");
|
||||||
for (PeerConnection peerConnection: activeConnections)
|
for (PeerConnection peerConnection: activeConnections)
|
||||||
{
|
{
|
||||||
if (peerConnection.getNode() == peer)
|
if (peerConnection.getNode().equals(peer))
|
||||||
return peerConnection;
|
return peerConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,14 +97,14 @@ public class ConnectionManager extends Thread
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return getNodeConnection(address);
|
return getNodeConnection(address, peer);
|
||||||
}
|
}
|
||||||
catch (IOException ex)
|
catch (IOException ex)
|
||||||
{
|
{
|
||||||
System.err.println("Couldn't create PeerConnection to " + address + " : " + ex.getMessage());
|
System.err.println("ConnectionManager: Couldn't create PeerConnection to " + address + " : " + ex.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
System.err.println("Failed to create PeerConnection to " + peer + "!");
|
System.err.println("ConnectionManager: Failed to create PeerConnection to " + peer + "!");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,8 +123,11 @@ public class ConnectionManager extends Thread
|
||||||
for (PeerConnection peerConnection: activeConnections)
|
for (PeerConnection peerConnection: activeConnections)
|
||||||
{
|
{
|
||||||
if (!peerConnection.isAlive())
|
if (!peerConnection.isAlive())
|
||||||
|
{
|
||||||
|
System.out.println("ConnectionManager: purged dead connection to " + peerConnection.getUri());
|
||||||
deadConnections.add(peerConnection);
|
deadConnections.add(peerConnection);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
activeConnections.removeAll(deadConnections);
|
activeConnections.removeAll(deadConnections);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,9 @@ public class FilePieceAccess implements Closeable
|
||||||
randomAccessFile.setLength(file.length());
|
randomAccessFile.setLength(file.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPieceOffset(int index)
|
public long getPieceOffset(int index)
|
||||||
{
|
{
|
||||||
return Math.toIntExact(index * networkFile.getPieceSize());
|
return (index * networkFile.getPieceSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPieceSize(int index)
|
public int getPieceSize(int index)
|
||||||
|
@ -47,7 +47,7 @@ public class FilePieceAccess implements Closeable
|
||||||
|
|
||||||
int pieceSize = getPieceSize(index);
|
int pieceSize = getPieceSize(index);
|
||||||
byte[] buffer = new byte[pieceSize];
|
byte[] buffer = new byte[pieceSize];
|
||||||
int pieceOffset = getPieceOffset(index);
|
long pieceOffset = getPieceOffset(index);
|
||||||
System.out.println("Reading piece " + index + " from file " + file.getName() + " (offset=" + pieceOffset + ", size=" + pieceSize + ")");
|
System.out.println("Reading piece " + index + " from file " + file.getName() + " (offset=" + pieceOffset + ", size=" + pieceSize + ")");
|
||||||
randomAccessFile.seek(pieceOffset);
|
randomAccessFile.seek(pieceOffset);
|
||||||
randomAccessFile.read(buffer);
|
randomAccessFile.read(buffer);
|
||||||
|
|
|
@ -74,7 +74,7 @@ public class FileRemoteAccess
|
||||||
|
|
||||||
if (!neededPieces.isEmpty())
|
if (!neededPieces.isEmpty())
|
||||||
{
|
{
|
||||||
boolean ok = waitForPieceRange(neededPieces, 10000);
|
boolean ok = waitForPieceRange(neededPieces, 1000);
|
||||||
if (!ok)
|
if (!ok)
|
||||||
{
|
{
|
||||||
System.err.println("FRA: timed out while waiting for pieces " + neededPieces);
|
System.err.println("FRA: timed out while waiting for pieces " + neededPieces);
|
||||||
|
|
|
@ -24,12 +24,15 @@ 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.DataStore;
|
||||||
import moe.nekojimi.friendcloud.storage.Model;
|
import moe.nekojimi.friendcloud.storage.Model;
|
||||||
import moe.nekojimi.friendcloud.storage.StupidJSONFileStore;
|
import moe.nekojimi.friendcloud.storage.StupidJSONFileStore;
|
||||||
import moe.nekojimi.friendcloud.tasks.JoinNetworkTask;
|
import moe.nekojimi.friendcloud.tasks.JoinNetworkTask;
|
||||||
import org.slf4j.simple.SimpleLogger;
|
import org.slf4j.simple.SimpleLogger;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
|
@ -60,12 +63,16 @@ public class Main
|
||||||
@Parameter(names="-create-network")
|
@Parameter(names="-create-network")
|
||||||
private boolean createNetwork = false;
|
private boolean createNetwork = false;
|
||||||
|
|
||||||
|
@Parameter(names = "-storage")
|
||||||
|
private String storageLocation = ".";
|
||||||
|
|
||||||
// @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")));
|
private Model model;
|
||||||
|
private final NotificationManager notificationManager = new NotificationManager();
|
||||||
|
|
||||||
public static void main(String[] args)
|
public static void main(String[] args)
|
||||||
{
|
{
|
||||||
|
@ -77,8 +84,9 @@ public class Main
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
instance.run();
|
instance.run();
|
||||||
} catch (IOException | InterruptedException | JLibnotifyLoadException | JLibnotifyInitException e)
|
} catch (Exception e)
|
||||||
{
|
{
|
||||||
|
System.err.println("main() received exception, dying horribly!!");
|
||||||
e.printStackTrace(System.err);
|
e.printStackTrace(System.err);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -94,6 +102,10 @@ public class Main
|
||||||
|
|
||||||
private void run() throws IOException, InterruptedException, JLibnotifyLoadException, JLibnotifyInitException
|
private void run() throws IOException, InterruptedException, JLibnotifyLoadException, JLibnotifyInitException
|
||||||
{
|
{
|
||||||
|
DataStore dataStore = new StupidJSONFileStore(new File(storageLocation));
|
||||||
|
model = new Model(dataStore);
|
||||||
|
|
||||||
|
model.init();
|
||||||
connectionManager = new ConnectionManager(tcpPort);
|
connectionManager = new ConnectionManager(tcpPort);
|
||||||
|
|
||||||
Path mountPoint;
|
Path mountPoint;
|
||||||
|
@ -106,7 +118,6 @@ public class Main
|
||||||
mountPoint = Path.of(System.getProperty("user.dir") + "/fuse-mount-" + tcpPort);
|
mountPoint = Path.of(System.getProperty("user.dir") + "/fuse-mount-" + tcpPort);
|
||||||
boolean created = mountPoint.toFile().mkdirs();
|
boolean created = mountPoint.toFile().mkdirs();
|
||||||
System.out.println("Created FUSE mount point " + mountPoint);
|
System.out.println("Created FUSE mount point " + mountPoint);
|
||||||
|
|
||||||
}
|
}
|
||||||
fuseAccess.mount(mountPoint);
|
fuseAccess.mount(mountPoint);
|
||||||
System.out.println("Mounted virtual filesystem at " + mountPoint);
|
System.out.println("Mounted virtual filesystem at " + mountPoint);
|
||||||
|
@ -122,10 +133,11 @@ public class Main
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
DefaultJLibnotify libnotify = (DefaultJLibnotify) DefaultJLibnotifyLoader.init().load();
|
// if (Desktop.isDesktopSupported())
|
||||||
libnotify.init("FriendCloud");
|
// {
|
||||||
JLibnotifyNotification notification = libnotify.createNotification("Holy balls a notification!", "Woah!!!", "dialog-information");
|
// Desktop desktop = Desktop.getDesktop();
|
||||||
notification.show();
|
// desktop.browse(mountPoint.toFile().toURI());
|
||||||
|
// }
|
||||||
|
|
||||||
connectionManager.addNewConnectionConsumer(this::requestCompleteState);
|
connectionManager.addNewConnectionConsumer(this::requestCompleteState);
|
||||||
|
|
||||||
|
@ -184,8 +196,8 @@ public class Main
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JoinNetworkTask joinNetworkTask = new JoinNetworkTask();
|
// JoinNetworkTask joinNetworkTask = new JoinNetworkTask();
|
||||||
executor.submit(joinNetworkTask);
|
// executor.submit(joinNetworkTask);
|
||||||
|
|
||||||
for (String knownPeerAddress : knownPeers)
|
for (String knownPeerAddress : knownPeers)
|
||||||
{
|
{
|
||||||
|
@ -309,4 +321,9 @@ public class Main
|
||||||
{
|
{
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NotificationManager getNotificationManager()
|
||||||
|
{
|
||||||
|
return notificationManager;
|
||||||
|
}
|
||||||
}
|
}
|
105
src/main/java/moe/nekojimi/friendcloud/NotificationManager.java
Normal file
105
src/main/java/moe/nekojimi/friendcloud/NotificationManager.java
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
package moe.nekojimi.friendcloud;
|
||||||
|
|
||||||
|
import es.blackleg.jlibnotify.JLibnotify;
|
||||||
|
import es.blackleg.jlibnotify.JLibnotifyNotification;
|
||||||
|
import es.blackleg.jlibnotify.core.DefaultJLibnotifyLoader;
|
||||||
|
import es.blackleg.jlibnotify.exception.JLibnotifyInitException;
|
||||||
|
import es.blackleg.jlibnotify.exception.JLibnotifyLoadException;
|
||||||
|
|
||||||
|
public class NotificationManager
|
||||||
|
{
|
||||||
|
private final JLibnotify libnotify;
|
||||||
|
|
||||||
|
public NotificationManager()
|
||||||
|
{
|
||||||
|
JLibnotify n;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
n = DefaultJLibnotifyLoader.init().load();
|
||||||
|
n.init("FriendCloud");
|
||||||
|
System.out.println("Libnotify capabilities detected: " + n.getServerCapabilities());
|
||||||
|
JLibnotifyNotification notification = n.createNotification("FriendCloud started", "It works! Cool!", "dialog-information");
|
||||||
|
notification.show();
|
||||||
|
} catch (JLibnotifyLoadException | JLibnotifyInitException e)
|
||||||
|
{
|
||||||
|
n = null;
|
||||||
|
System.err.println("Failed to initialise notification manager.");
|
||||||
|
e.printStackTrace(System.err);
|
||||||
|
}
|
||||||
|
libnotify = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum NotificationType
|
||||||
|
{
|
||||||
|
HELLO,
|
||||||
|
TRANSFER_IN_PROGRESS,
|
||||||
|
TRANSFER_DONE,
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getNotificationIcon(NotificationType type)
|
||||||
|
{
|
||||||
|
return switch (type)
|
||||||
|
{
|
||||||
|
default -> "dialog-information";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Notification createNotification(String heading, String body, NotificationType type)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return new Notification(heading, body, type);
|
||||||
|
} catch (Exception e)
|
||||||
|
{
|
||||||
|
e.printStackTrace(System.err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Notification
|
||||||
|
{
|
||||||
|
private String heading;
|
||||||
|
private String body;
|
||||||
|
private NotificationType type;
|
||||||
|
|
||||||
|
private JLibnotifyNotification notification;
|
||||||
|
|
||||||
|
public Notification(String heading, String body, NotificationType type)
|
||||||
|
{
|
||||||
|
this.heading = heading;
|
||||||
|
this.body = body;
|
||||||
|
this.type = type;
|
||||||
|
|
||||||
|
if (libnotify != null)
|
||||||
|
{
|
||||||
|
notification = libnotify.createNotification(heading, body, getNotificationIcon(type));
|
||||||
|
// notification.setTimeOut(10);
|
||||||
|
notification.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHeading(String heading)
|
||||||
|
{
|
||||||
|
this.heading = heading;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBody(String body)
|
||||||
|
{
|
||||||
|
this.body = body;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(NotificationType type)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update()
|
||||||
|
{
|
||||||
|
notification.update(heading, body, getNotificationIcon(type));
|
||||||
|
// notification.setTimeOut(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
package moe.nekojimi.friendcloud;
|
package moe.nekojimi.friendcloud;
|
||||||
|
|
||||||
import moe.nekojimi.friendcloud.objects.NetworkObject;
|
import moe.nekojimi.friendcloud.objects.ObjectID;
|
||||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||||
import moe.nekojimi.friendcloud.storage.Storable;
|
import moe.nekojimi.friendcloud.storage.Storable;
|
||||||
|
|
||||||
|
@ -13,18 +13,13 @@ public class ObjectChangeRecord implements Storable
|
||||||
{
|
{
|
||||||
// private final long changeID;
|
// private final long changeID;
|
||||||
|
|
||||||
private NetworkObject.ObjectID creatorPeer;
|
private ObjectID creatorPeer;
|
||||||
private Set<Long> changeHeads = new HashSet<>();
|
private Set<Long> changeHeads = new HashSet<>();
|
||||||
private Set<Change> changes = new HashSet<>();
|
private Set<Change> changes = new HashSet<>();
|
||||||
|
|
||||||
public ObjectChangeRecord(NetworkObject.ObjectID creatorPeer)
|
|
||||||
{
|
|
||||||
this.creatorPeer = creatorPeer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ObjectChangeRecord createFromChangeMessage(ObjectStatements.ObjectChangeMessage objectChangeMessage)
|
public static ObjectChangeRecord createFromChangeMessage(ObjectStatements.ObjectChangeMessage objectChangeMessage)
|
||||||
{
|
{
|
||||||
ObjectChangeRecord record = new ObjectChangeRecord(new NetworkObject.ObjectID(0)); // TODO: decode creator
|
ObjectChangeRecord record = new ObjectChangeRecord(); // TODO: decode creator
|
||||||
record.changeHeads.addAll(objectChangeMessage.getChangeHeadsList());
|
record.changeHeads.addAll(objectChangeMessage.getChangeHeadsList());
|
||||||
for (ObjectStatements.ObjectChange objectChange : objectChangeMessage.getChangesList())
|
for (ObjectStatements.ObjectChange objectChange : objectChangeMessage.getChangesList())
|
||||||
{
|
{
|
||||||
|
@ -39,9 +34,10 @@ public class ObjectChangeRecord implements Storable
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ObjectChangeRecord createFromChanges(NetworkObject.ObjectID creator, Set<Change> changes)
|
public static ObjectChangeRecord createFromChanges(ObjectID creator, Set<Change> changes)
|
||||||
{
|
{
|
||||||
ObjectChangeRecord record = new ObjectChangeRecord(creator);
|
ObjectChangeRecord record = new ObjectChangeRecord();
|
||||||
|
record.creatorPeer = creator;
|
||||||
record.changes.addAll(changes);
|
record.changes.addAll(changes);
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
@ -83,7 +79,7 @@ public class ObjectChangeRecord implements Storable
|
||||||
{
|
{
|
||||||
changeHeads = new HashSet<>((Collection) map.get("changeHeads"));
|
changeHeads = new HashSet<>((Collection) map.get("changeHeads"));
|
||||||
changes = new HashSet<>((Collection) map.get("changes"));
|
changes = new HashSet<>((Collection) map.get("changes"));
|
||||||
creatorPeer = new NetworkObject.ObjectID((Long) map.get("creator"));
|
creatorPeer = new ObjectID((Long) map.get("creator"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString()
|
public String toString()
|
||||||
|
@ -115,7 +111,7 @@ public class ObjectChangeRecord implements Storable
|
||||||
return Util.xorBytesToLong(bytes);
|
return Util.xorBytesToLong(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NetworkObject.ObjectID getCreatorPeer()
|
public ObjectID getCreatorPeer()
|
||||||
{
|
{
|
||||||
return creatorPeer;
|
return creatorPeer;
|
||||||
}
|
}
|
||||||
|
@ -131,12 +127,12 @@ public class ObjectChangeRecord implements Storable
|
||||||
return changeHeads;
|
return changeHeads;
|
||||||
}
|
}
|
||||||
|
|
||||||
public record Change(NetworkObject.ObjectID objectID, Map<String, String> beforeValues, Map<String, String> afterValues)
|
public record Change(ObjectID objectID, Map<String, String> beforeValues, Map<String, String> afterValues)
|
||||||
{
|
{
|
||||||
|
|
||||||
public static Change createFromObjectChange(ObjectStatements.ObjectChange change)
|
public static Change createFromObjectChange(ObjectStatements.ObjectChange change)
|
||||||
{
|
{
|
||||||
return new Change(new NetworkObject.ObjectID(change.getObjectId()), change.getBeforeMap(), change.getAfterMap());
|
return new Change(new ObjectID(change.getObjectId()), change.getBeforeMap(), change.getAfterMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Change createFromObjectStates(ObjectStatements.ObjectState before, ObjectStatements.ObjectState after)
|
public static Change createFromObjectStates(ObjectStatements.ObjectState before, ObjectStatements.ObjectState after)
|
||||||
|
@ -155,7 +151,7 @@ public class ObjectChangeRecord implements Storable
|
||||||
}
|
}
|
||||||
if (!afterValues.isEmpty())
|
if (!afterValues.isEmpty())
|
||||||
{
|
{
|
||||||
return new Change(new NetworkObject.ObjectID(before.getObjectId()), beforeValues, afterValues);
|
return new Change(new ObjectID(before.getObjectId()), beforeValues, afterValues);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package moe.nekojimi.friendcloud;
|
package moe.nekojimi.friendcloud;
|
||||||
|
|
||||||
import moe.nekojimi.friendcloud.objects.NetworkObject;
|
import moe.nekojimi.friendcloud.objects.NetworkObject;
|
||||||
|
import moe.nekojimi.friendcloud.objects.ObjectID;
|
||||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||||
import moe.nekojimi.friendcloud.tasks.PropagateMessageTask;
|
import moe.nekojimi.friendcloud.tasks.PropagateMessageTask;
|
||||||
|
|
||||||
|
@ -13,29 +14,29 @@ import java.util.Set;
|
||||||
|
|
||||||
public class ObjectChangeTransaction implements Closeable
|
public class ObjectChangeTransaction implements Closeable
|
||||||
{
|
{
|
||||||
private final NetworkObject.ObjectID creator;
|
private final ObjectID creator;
|
||||||
private final ConnectionManager connectionManager;
|
private final ConnectionManager connectionManager;
|
||||||
private final Map<NetworkObject.ObjectID, ObjectStatements.ObjectState> beforeStates = new HashMap<>();
|
private final Map<ObjectID, ObjectStatements.ObjectState> beforeStates = new HashMap<>();
|
||||||
|
|
||||||
private boolean ended = false;
|
private boolean ended = false;
|
||||||
|
|
||||||
ObjectChangeTransaction(ConnectionManager connectionManager, NetworkObject.ObjectID creator)
|
ObjectChangeTransaction(ConnectionManager connectionManager, ObjectID creator)
|
||||||
{
|
{
|
||||||
this.creator = creator;
|
this.creator = creator;
|
||||||
this.connectionManager = connectionManager;
|
this.connectionManager = connectionManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ObjectChangeTransaction startTransaction(ConnectionManager connectionManager, NetworkObject.ObjectID creatorPeer, NetworkObject.ObjectID... objects)
|
public static ObjectChangeTransaction startTransaction(ConnectionManager connectionManager, ObjectID creatorPeer, ObjectID... objects)
|
||||||
{
|
{
|
||||||
ObjectChangeTransaction builder = new ObjectChangeTransaction(connectionManager, creatorPeer);
|
ObjectChangeTransaction builder = new ObjectChangeTransaction(connectionManager, creatorPeer);
|
||||||
for (NetworkObject.ObjectID id : objects)
|
for (ObjectID id : objects)
|
||||||
{
|
{
|
||||||
builder.addObjectBeforeChange(id);
|
builder.addObjectBeforeChange(id);
|
||||||
}
|
}
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObjectChangeTransaction addObjectBeforeChange(NetworkObject.ObjectID id)
|
public ObjectChangeTransaction addObjectBeforeChange(ObjectID id)
|
||||||
{
|
{
|
||||||
NetworkObject object = Main.getInstance().getModel().getObject(id);
|
NetworkObject object = Main.getInstance().getModel().getObject(id);
|
||||||
if (object != null)
|
if (object != null)
|
||||||
|
@ -50,7 +51,7 @@ public class ObjectChangeTransaction implements Closeable
|
||||||
ended = true;
|
ended = true;
|
||||||
Set<ObjectChangeRecord.Change> changes = new HashSet<>();
|
Set<ObjectChangeRecord.Change> changes = new HashSet<>();
|
||||||
|
|
||||||
for (Map.Entry<NetworkObject.ObjectID, ObjectStatements.ObjectState> entry : beforeStates.entrySet())
|
for (Map.Entry<ObjectID, ObjectStatements.ObjectState> entry : beforeStates.entrySet())
|
||||||
{
|
{
|
||||||
ObjectStatements.ObjectState afterState = Main.getInstance().getModel().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);
|
||||||
|
|
|
@ -17,4 +17,16 @@ public class Util
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static long unconditionalNumberToLong(Object number)
|
||||||
|
{
|
||||||
|
assert (number instanceof Number);
|
||||||
|
return ((Number)number).longValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double unconditionalNumberToDouble(Object number)
|
||||||
|
{
|
||||||
|
assert (number instanceof Number);
|
||||||
|
return ((Number)number).doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import moe.nekojimi.friendcloud.FilePieceAccess;
|
||||||
import moe.nekojimi.friendcloud.Main;
|
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.ObjectID;
|
||||||
import moe.nekojimi.friendcloud.objects.Peer;
|
import moe.nekojimi.friendcloud.objects.Peer;
|
||||||
import moe.nekojimi.friendcloud.protos.CommonMessages;
|
import moe.nekojimi.friendcloud.protos.CommonMessages;
|
||||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||||
|
@ -129,9 +130,9 @@ public abstract class PeerConnection extends Thread
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkObject.ObjectID senderID = new NetworkObject.ObjectID(header.getSenderId());
|
ObjectID senderID = new ObjectID(header.getSenderId());
|
||||||
if (peer == null)
|
if (peer == null)
|
||||||
peer = (Peer) Main.getInstance().getModel().getOrCreateObject(senderID);
|
peer = Main.getInstance().getModel().getOrCreateObject(senderID);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!senderID.equals(peer.getObjectID()))
|
if (!senderID.equals(peer.getObjectID()))
|
||||||
|
@ -194,7 +195,7 @@ public abstract class PeerConnection extends Thread
|
||||||
{
|
{
|
||||||
objectList.addStates(object.buildObjectState());
|
objectList.addStates(object.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));
|
||||||
}
|
}
|
||||||
else if (body.is(PieceMessages.FilePiecesRequestMessage.class))
|
else if (body.is(PieceMessages.FilePiecesRequestMessage.class))
|
||||||
|
@ -205,7 +206,7 @@ public abstract class PeerConnection extends Thread
|
||||||
replyWithError(CommonMessages.Error.ERROR_INVALID_ARGUMENT, header);
|
replyWithError(CommonMessages.Error.ERROR_INVALID_ARGUMENT, header);
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkFile networkFile = (NetworkFile) Main.getInstance().getModel().getObject(new NetworkObject.ObjectID(filePiecesRequestMessage.getFileId()));
|
NetworkFile networkFile = (NetworkFile) Main.getInstance().getModel().getObject(new 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);
|
||||||
|
|
|
@ -47,10 +47,13 @@ public class PeerTCPConnection extends PeerConnection
|
||||||
messageReceived(message);
|
messageReceived(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException ex)
|
} catch (Exception ex)
|
||||||
{
|
{
|
||||||
// fuck
|
// fuck
|
||||||
|
ex.printStackTrace(System.err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
System.out.println("TCP Connection: connection closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -4,6 +4,7 @@ import com.google.protobuf.Any;
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
import moe.nekojimi.friendcloud.Main;
|
import moe.nekojimi.friendcloud.Main;
|
||||||
import moe.nekojimi.friendcloud.objects.NetworkObject;
|
import moe.nekojimi.friendcloud.objects.NetworkObject;
|
||||||
|
import moe.nekojimi.friendcloud.objects.ObjectID;
|
||||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -37,15 +38,16 @@ public class ObjectListRequest extends Request<ObjectStatements.ObjectListReques
|
||||||
{
|
{
|
||||||
ObjectStatements.ObjectList objectList = reply.unpack(ObjectStatements.ObjectList.class);
|
ObjectStatements.ObjectList objectList = reply.unpack(ObjectStatements.ObjectList.class);
|
||||||
|
|
||||||
System.out.println("Received ObjectList, objects=" + objectList.getStatesList());
|
// System.out.println("Received ObjectList, objects=" + objectList.getStatesList());
|
||||||
|
|
||||||
List<NetworkObject> ret = new ArrayList<>();
|
List<NetworkObject> ret = new ArrayList<>();
|
||||||
|
|
||||||
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 = Main.getInstance().getModel().getOrCreateObject(new NetworkObject.ObjectID(objectState.getObjectId()));
|
NetworkObject object = Main.getInstance().getModel().getOrCreateObject(new ObjectID(objectState.getObjectId()));
|
||||||
object.updateFromStateMessage(objectState);
|
object.updateFromStateMessage(objectState);
|
||||||
|
Main.getInstance().getModel().objectChanged(object);
|
||||||
ret.add(object);
|
ret.add(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
package moe.nekojimi.friendcloud.objects;
|
package moe.nekojimi.friendcloud.objects;
|
||||||
|
|
||||||
|
import moe.nekojimi.friendcloud.Main;
|
||||||
|
import moe.nekojimi.friendcloud.Util;
|
||||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
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.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class NetworkFile extends NetworkFSNode
|
public class NetworkFile extends NetworkFSNode
|
||||||
{
|
{
|
||||||
|
@ -26,7 +27,8 @@ public class NetworkFile extends NetworkFSNode
|
||||||
|
|
||||||
private File localFile = null;
|
private File localFile = null;
|
||||||
|
|
||||||
private final Map<Peer, PeerFileState> fileStates = new HashMap<>();
|
private final SortedSet<ObjectID> peersWithCopy = new TreeSet<>();
|
||||||
|
// private final Map<Peer, PeerFileState> fileStates = new HashMap<>();
|
||||||
private BitSet pieces = new BitSet();
|
private BitSet pieces = new BitSet();
|
||||||
|
|
||||||
// private List<FilePiece> pieces = new ArrayList<>();
|
// private List<FilePiece> pieces = new ArrayList<>();
|
||||||
|
@ -97,6 +99,11 @@ public class NetworkFile extends NetworkFSNode
|
||||||
System.out.println("Total hash: " + HexFormat.of().formatHex(hash));
|
System.out.println("Total hash: " + HexFormat.of().formatHex(hash));
|
||||||
System.out.println("Have " + pieces.cardinality() + " of " + getPieceCount() + " pieces.");
|
System.out.println("Have " + pieces.cardinality() + " of " + getPieceCount() + " pieces.");
|
||||||
|
|
||||||
|
if (pieces.cardinality() >= getPieceCount())
|
||||||
|
{
|
||||||
|
peersWithCopy.add(Main.getInstance().getModel().getSelfPeer().getObjectID());
|
||||||
|
}
|
||||||
|
|
||||||
} catch (NoSuchAlgorithmException e)
|
} catch (NoSuchAlgorithmException e)
|
||||||
{
|
{
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
@ -115,6 +122,15 @@ 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"));
|
||||||
|
if (state.containsValues("peersWithCopy"))
|
||||||
|
{
|
||||||
|
peersWithCopy.clear();
|
||||||
|
String[] peers = state.getValuesOrThrow("peersWithCopy").split(",");
|
||||||
|
for (String peer: peers)
|
||||||
|
{
|
||||||
|
peersWithCopy.add(new ObjectID(Long.parseUnsignedLong(peer,16)));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -124,7 +140,8 @@ public class NetworkFile extends NetworkFSNode
|
||||||
// .putValues("path", path)
|
// .putValues("path", path)
|
||||||
.putValues("size", Long.toString(size))
|
.putValues("size", Long.toString(size))
|
||||||
.putValues("hash", HexFormat.of().formatHex(hash))
|
.putValues("hash", HexFormat.of().formatHex(hash))
|
||||||
.putValues("pieceSize", Long.toString(pieceSize));
|
.putValues("pieceSize", Long.toString(pieceSize))
|
||||||
|
.putValues("peersWithCopy", peersWithCopy.stream().map(ObjectID::toLong).map(Long::toHexString).collect(Collectors.joining(",")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -136,6 +153,7 @@ public class NetworkFile extends NetworkFSNode
|
||||||
ret.put("pieceSize", pieceSize);
|
ret.put("pieceSize", pieceSize);
|
||||||
ret.put("pieces", Arrays.stream(pieces.toLongArray()).boxed().toList());
|
ret.put("pieces", Arrays.stream(pieces.toLongArray()).boxed().toList());
|
||||||
ret.put("localFile", localFile != null ? localFile.getAbsolutePath() : "");
|
ret.put("localFile", localFile != null ? localFile.getAbsolutePath() : "");
|
||||||
|
ret.put("peersWithCopy", peersWithCopy.stream().map(ObjectID::toLong).toList());
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,13 +164,14 @@ public class NetworkFile extends NetworkFSNode
|
||||||
size = ((Number) map.get("size")).longValue();
|
size = ((Number) map.get("size")).longValue();
|
||||||
hash = HexFormat.of().parseHex((CharSequence) map.get("hash"));
|
hash = HexFormat.of().parseHex((CharSequence) map.get("hash"));
|
||||||
pieceSize = ((Number) map.get("pieceSize")).longValue();
|
pieceSize = ((Number) map.get("pieceSize")).longValue();
|
||||||
ArrayList<Number> pieces1 = (ArrayList<Number>) map.get("pieces");
|
pieces = BitSet.valueOf(((ArrayList<Number>) map.get("pieces")).stream().mapToLong(Number::longValue).toArray());
|
||||||
pieces = BitSet.valueOf(pieces1.stream().mapToLong(Number::longValue).toArray());
|
|
||||||
String localFilePath = (String) map.get("localFile");
|
String localFilePath = (String) map.get("localFile");
|
||||||
if (localFilePath.isEmpty())
|
if (localFilePath.isEmpty())
|
||||||
localFile = null;
|
localFile = null;
|
||||||
else
|
else
|
||||||
localFile = new File(localFilePath);
|
localFile = new File(localFilePath);
|
||||||
|
peersWithCopy.clear();
|
||||||
|
peersWithCopy.addAll(((List<Object>)map.get("peersWithCopy")).stream().map(Util::unconditionalNumberToLong).map(ObjectID::new).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public File getLocalFile()
|
public File getLocalFile()
|
||||||
|
@ -241,7 +260,7 @@ public class NetworkFile extends NetworkFSNode
|
||||||
synchronized (pieces)
|
synchronized (pieces)
|
||||||
{
|
{
|
||||||
pieces.set(pieceIdx, has);
|
pieces.set(pieceIdx, has);
|
||||||
pieces.notifyAll();
|
notifyPieceWaiters();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,14 +274,24 @@ public class NetworkFile extends NetworkFSNode
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addFileState(PeerFileState peerFileState)
|
// void addFileState(PeerFileState peerFileState)
|
||||||
|
// {
|
||||||
|
// fileStates.put(peerFileState.getNode(), peerFileState);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public Map<Peer, PeerFileState> getFileStates()
|
||||||
|
// {
|
||||||
|
// return fileStates;
|
||||||
|
// }
|
||||||
|
|
||||||
|
public void addPeerWithCopy(Peer selfPeer)
|
||||||
{
|
{
|
||||||
fileStates.put(peerFileState.getNode(), peerFileState);
|
peersWithCopy.add(selfPeer.getObjectID());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Peer, PeerFileState> getFileStates()
|
public List<Peer> getPeersWithCopy()
|
||||||
{
|
{
|
||||||
return fileStates;
|
return peersWithCopy.stream().map(objectID -> (Peer) Main.getInstance().getModel().getObject(objectID)).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -301,6 +330,14 @@ public class NetworkFile extends NetworkFSNode
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void notifyPieceWaiters()
|
||||||
|
{
|
||||||
|
synchronized (pieces)
|
||||||
|
{
|
||||||
|
pieces.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public enum StorageType
|
public enum StorageType
|
||||||
{
|
{
|
||||||
/** The file will be stored as a complete file in the storage directory under it's own name and file path. */
|
/** The file will be stored as a complete file in the storage directory under it's own name and file path. */
|
||||||
|
|
|
@ -3,9 +3,6 @@ package moe.nekojimi.friendcloud.objects;
|
||||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.SortedSet;
|
|
||||||
import java.util.TreeSet;
|
|
||||||
import java.util.WeakHashMap;
|
|
||||||
|
|
||||||
public class NetworkFolder extends NetworkFSNode
|
public class NetworkFolder extends NetworkFSNode
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,12 +2,13 @@ package moe.nekojimi.friendcloud.objects;
|
||||||
|
|
||||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||||
import moe.nekojimi.friendcloud.storage.Storable;
|
import moe.nekojimi.friendcloud.storage.Storable;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public abstract class NetworkObject implements Storable
|
public abstract class NetworkObject implements Storable, Comparable<NetworkObject>
|
||||||
{
|
{
|
||||||
private final ObjectID objectID;
|
private final ObjectID objectID;
|
||||||
|
|
||||||
|
@ -53,67 +54,23 @@ public abstract class NetworkObject implements Storable
|
||||||
.setObjectId(objectID.toLong());
|
.setObjectId(objectID.toLong());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ObjectID
|
|
||||||
{
|
|
||||||
private final ObjectStatements.ObjectType type;
|
|
||||||
private final int systemID;
|
|
||||||
private final int uniqueID;
|
|
||||||
|
|
||||||
public ObjectID(long id)
|
|
||||||
{
|
|
||||||
uniqueID = (int)(0x00000000_FFFFFFFFL & id);
|
|
||||||
systemID = Math.toIntExact((0x00FFFFFF_00000000L & id) >>> 32);
|
|
||||||
type = ObjectStatements.ObjectType.forNumber(Math.toIntExact(((0xFF000000_00000000L & id) >>> 56)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectID(ObjectStatements.ObjectType type, int systemID, int uniqueID)
|
|
||||||
{
|
|
||||||
this.type = type;
|
|
||||||
this.systemID = systemID;
|
|
||||||
this.uniqueID = uniqueID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long toLong()
|
|
||||||
{
|
|
||||||
long uniquePart = Integer.toUnsignedLong(uniqueID);
|
|
||||||
long systemPart = Integer.toUnsignedLong(systemID) << 32;
|
|
||||||
long typePart = ((long) type.getNumber()) << 56;
|
|
||||||
return typePart | systemPart | uniquePart;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectStatements.ObjectType getType()
|
|
||||||
{
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getSystemID()
|
|
||||||
{
|
|
||||||
return systemID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getUniqueID()
|
|
||||||
{
|
|
||||||
return uniqueID;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public int compareTo(@NotNull NetworkObject networkObject)
|
||||||
{
|
{
|
||||||
return "OBJ{" + Long.toHexString(toLong()) + "}";
|
return Long.compare(getObjectID().toLong(), networkObject.getObjectID().toLong());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o)
|
public boolean equals(Object o)
|
||||||
{
|
{
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
ObjectID objectID = (ObjectID) o;
|
NetworkObject that = (NetworkObject) o;
|
||||||
return systemID == objectID.systemID && uniqueID == objectID.uniqueID && type == objectID.type;
|
return Objects.equals(objectID, that.objectID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode()
|
public int hashCode()
|
||||||
{
|
{
|
||||||
return Objects.hash(type, systemID, uniqueID);
|
return Objects.hashCode(objectID);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
75
src/main/java/moe/nekojimi/friendcloud/objects/ObjectID.java
Normal file
75
src/main/java/moe/nekojimi/friendcloud/objects/ObjectID.java
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
package moe.nekojimi.friendcloud.objects;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class ObjectID implements Comparable<ObjectID>
|
||||||
|
{
|
||||||
|
private final moe.nekojimi.friendcloud.protos.ObjectStatements.ObjectType type;
|
||||||
|
private final int systemID;
|
||||||
|
private final int uniqueID;
|
||||||
|
|
||||||
|
public ObjectID(long id)
|
||||||
|
{
|
||||||
|
uniqueID = (int) (0x00000000_FFFFFFFFL & id);
|
||||||
|
systemID = Math.toIntExact((0x00FFFFFF_00000000L & id) >>> 32);
|
||||||
|
type = moe.nekojimi.friendcloud.protos.ObjectStatements.ObjectType.forNumber(Math.toIntExact(((0xFF000000_00000000L & id) >>> 56)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectID(moe.nekojimi.friendcloud.protos.ObjectStatements.ObjectType type, int systemID, int uniqueID)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
this.systemID = systemID;
|
||||||
|
this.uniqueID = uniqueID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long toLong()
|
||||||
|
{
|
||||||
|
long uniquePart = Integer.toUnsignedLong(uniqueID);
|
||||||
|
long systemPart = Integer.toUnsignedLong(systemID) << 32;
|
||||||
|
long typePart = ((long) type.getNumber()) << 56;
|
||||||
|
return typePart | systemPart | uniquePart;
|
||||||
|
}
|
||||||
|
|
||||||
|
public moe.nekojimi.friendcloud.protos.ObjectStatements.ObjectType getType()
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSystemID()
|
||||||
|
{
|
||||||
|
return systemID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getUniqueID()
|
||||||
|
{
|
||||||
|
return uniqueID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "OBJ{" + Integer.toHexString(type.getNumber()) + "-" + Integer.toHexString(systemID) + "-" + Integer.toHexString(uniqueID) + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o)
|
||||||
|
{
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
ObjectID objectID = (ObjectID) o;
|
||||||
|
return systemID == objectID.systemID && uniqueID == objectID.uniqueID && type == objectID.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode()
|
||||||
|
{
|
||||||
|
return Objects.hash(type, systemID, uniqueID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(@NotNull ObjectID objectID)
|
||||||
|
{
|
||||||
|
return Long.compare(toLong(), objectID.toLong());
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,11 +9,11 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class Peer extends NetworkObject
|
public class Peer extends NetworkObject
|
||||||
{
|
{
|
||||||
private final List<URI> addresses = new ArrayList<>();
|
private final SortedSet<URI> addresses = new TreeSet<>();
|
||||||
private String userName = "";
|
private String userName = "";
|
||||||
private String systemName = "";
|
private String systemName = "";
|
||||||
|
|
||||||
private Map<NetworkFile, PeerFileState> fileStates = new HashMap<>();
|
// private Map<NetworkFile, PeerFileState> fileStates = new HashMap<>();
|
||||||
|
|
||||||
private volatile int lastTriedAddressIdx = -1;
|
private volatile int lastTriedAddressIdx = -1;
|
||||||
|
|
||||||
|
@ -94,25 +94,17 @@ public class Peer extends NetworkObject
|
||||||
addresses.add(address);
|
addresses.add(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
public URI getNextAddress()
|
// void addFileState(PeerFileState peerFileState)
|
||||||
{
|
// {
|
||||||
lastTriedAddressIdx++;
|
// fileStates.put(peerFileState.getFile(), peerFileState);
|
||||||
if (lastTriedAddressIdx >= addresses.size())
|
// }
|
||||||
lastTriedAddressIdx = 0;
|
//
|
||||||
return addresses.get(lastTriedAddressIdx);
|
// public Map<NetworkFile, PeerFileState> getFileStates()
|
||||||
}
|
// {
|
||||||
|
// return fileStates;
|
||||||
|
// }
|
||||||
|
|
||||||
void addFileState(PeerFileState peerFileState)
|
public SortedSet<URI> getAddresses()
|
||||||
{
|
|
||||||
fileStates.put(peerFileState.getFile(), peerFileState);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<NetworkFile, PeerFileState> getFileStates()
|
|
||||||
{
|
|
||||||
return fileStates;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<URI> getAddresses()
|
|
||||||
{
|
{
|
||||||
return addresses;
|
return addresses;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
package moe.nekojimi.friendcloud.objects;
|
package moe.nekojimi.friendcloud.objects;
|
||||||
|
|
||||||
import moe.nekojimi.friendcloud.Main;
|
import moe.nekojimi.friendcloud.Util;
|
||||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class PeerFileState extends NetworkObject
|
public class PeerFileState extends NetworkObject
|
||||||
{
|
{
|
||||||
private Peer peer;
|
private ObjectID peerID;
|
||||||
private NetworkFile file;
|
private ObjectID fileID;
|
||||||
|
|
||||||
private double progress = 0;
|
private double progress = 0;
|
||||||
|
|
||||||
|
@ -21,13 +21,10 @@ 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) Main.getInstance().getModel().getOrCreateObject(new ObjectID(Long.parseLong(state.getValuesOrThrow("peer"))));
|
peerID = new ObjectID(Long.parseLong(state.getValuesOrThrow("peer")));
|
||||||
file = (NetworkFile) Main.getInstance().getModel().getOrCreateObject(new ObjectID(Long.parseLong(state.getValuesOrThrow("file"))));
|
fileID = 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"));
|
||||||
|
|
||||||
peer.addFileState(this);
|
|
||||||
file.addFileState(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -40,20 +37,20 @@ public class PeerFileState extends NetworkObject
|
||||||
public ObjectStatements.ObjectState.Builder buildObjectState()
|
public ObjectStatements.ObjectState.Builder buildObjectState()
|
||||||
{
|
{
|
||||||
return super.buildObjectState()
|
return super.buildObjectState()
|
||||||
.putValues("peer", Long.toString(peer.getObjectID().toLong()))
|
// .putValues("peer", Long.toString(peer.getObjectID().toLong()))
|
||||||
.putValues("file", Long.toString(file.getObjectID().toLong()))
|
// .putValues("file", Long.toString(file.getObjectID().toLong()))
|
||||||
.putValues("progress", Double.toString(progress));
|
.putValues("progress", Double.toString(progress));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setNode(Peer peer)
|
// public void setNode(Peer peer)
|
||||||
{
|
// {
|
||||||
this.peer = peer;
|
// this.peer = peer;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
public void setFile(NetworkFile file)
|
// public void setFile(NetworkFile file)
|
||||||
{
|
// {
|
||||||
this.file = file;
|
// this.file = file;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public double getProgress()
|
public double getProgress()
|
||||||
{
|
{
|
||||||
|
@ -65,19 +62,31 @@ public class PeerFileState extends NetworkObject
|
||||||
this.progress = progress;
|
this.progress = progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public NetworkFile getFile()
|
// public NetworkFile getFile()
|
||||||
{
|
// {
|
||||||
return file;
|
// return file;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
// public Peer getNode()
|
||||||
|
// {
|
||||||
|
// return peer;
|
||||||
|
// }
|
||||||
|
|
||||||
public Peer getNode()
|
@Override
|
||||||
|
public Map<String, Object> getStateMap()
|
||||||
{
|
{
|
||||||
return peer;
|
Map<String, Object> ret = super.getStateMap();
|
||||||
|
// ret.put("peer", peer == null ? 0L : peer.getObjectID().toLong());
|
||||||
|
// ret.put("file", file == null? 0L : file.getObjectID().toLong());
|
||||||
|
ret.put("progress", progress);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateFromStateMap(Map<String, Object> map)
|
public void updateFromStateMap(Map<String, Object> map)
|
||||||
{
|
{
|
||||||
|
// peer = Main.getInstance().getModel().getObject(new ObjectID(Util.unconditionalNumberToLong(map.get("peer"))));
|
||||||
|
// file = Main.getInstance().getModel().getObject(new ObjectID(Util.unconditionalNumberToLong(map.get("file"))));
|
||||||
|
progress = Util.unconditionalNumberToDouble( map.get("progress"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package moe.nekojimi.friendcloud.storage;
|
package moe.nekojimi.friendcloud.storage;
|
||||||
|
|
||||||
import moe.nekojimi.friendcloud.Main;
|
import moe.nekojimi.friendcloud.Main;
|
||||||
import moe.nekojimi.friendcloud.objects.NetworkObject;
|
import moe.nekojimi.friendcloud.ObjectChangeRecord;
|
||||||
|
import moe.nekojimi.friendcloud.Util;
|
||||||
|
import moe.nekojimi.friendcloud.objects.ObjectID;
|
||||||
import moe.nekojimi.friendcloud.objects.Peer;
|
import moe.nekojimi.friendcloud.objects.Peer;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -9,6 +11,8 @@ import java.util.Map;
|
||||||
public class LocalData implements Storable
|
public class LocalData implements Storable
|
||||||
{
|
{
|
||||||
private Peer localPeer;
|
private Peer localPeer;
|
||||||
|
private ObjectChangeRecord currentChangeRecord;
|
||||||
|
private int systemID;
|
||||||
|
|
||||||
public Peer getLocalPeer()
|
public Peer getLocalPeer()
|
||||||
{
|
{
|
||||||
|
@ -20,6 +24,26 @@ public class LocalData implements Storable
|
||||||
this.localPeer = localPeer;
|
this.localPeer = localPeer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ObjectChangeRecord getCurrentChangeRecord()
|
||||||
|
{
|
||||||
|
return currentChangeRecord;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentChangeRecord(ObjectChangeRecord currentChangeRecord)
|
||||||
|
{
|
||||||
|
this.currentChangeRecord = currentChangeRecord;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSystemID()
|
||||||
|
{
|
||||||
|
return systemID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSystemID(int systemID)
|
||||||
|
{
|
||||||
|
this.systemID = systemID;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getStorageID()
|
public long getStorageID()
|
||||||
{
|
{
|
||||||
|
@ -29,12 +53,17 @@ public class LocalData implements Storable
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> getStateMap()
|
public Map<String, Object> getStateMap()
|
||||||
{
|
{
|
||||||
return Map.of("localPeer", localPeer.getObjectID().toLong());
|
return Map.of("localPeer", localPeer == null ? 0L : localPeer.getObjectID().toLong(),
|
||||||
|
"currentChangeRecord", currentChangeRecord == null ? 0L : currentChangeRecord.getChangeID(),
|
||||||
|
"systemID", systemID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateFromStateMap(Map<String, Object> map)
|
public void updateFromStateMap(Map<String, Object> map)
|
||||||
{
|
{
|
||||||
localPeer = (Peer) Main.getInstance().getModel().getObject(new NetworkObject.ObjectID((Long) map.get("localPeer")));
|
localPeer = Main.getInstance().getModel().getObject(new ObjectID(Util.unconditionalNumberToLong(map.getOrDefault("localPeer",0))));
|
||||||
|
currentChangeRecord = Main.getInstance().getModel().getChangeRecord(Util.unconditionalNumberToLong(map.getOrDefault("currentChangeRecord",0)));
|
||||||
|
systemID = (int) map.getOrDefault("systemID", 0);
|
||||||
|
System.out.println("LocalData: resumed state, localPeer=" + localPeer + ", currentChangeRecord=" + currentChangeRecord + ", systemID=" + Integer.toHexString(systemID));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,37 +11,53 @@ public class Model
|
||||||
{
|
{
|
||||||
|
|
||||||
private final CachingDataStore dataStore;
|
private final CachingDataStore dataStore;
|
||||||
|
private LocalData localData;
|
||||||
private final int systemID;
|
|
||||||
|
|
||||||
private Peer selfPeer = null;
|
|
||||||
private ObjectChangeRecord currentChange;
|
|
||||||
|
|
||||||
public Model(DataStore dataStore)
|
public Model(DataStore dataStore)
|
||||||
{
|
{
|
||||||
this.dataStore = new CachingDataStore(dataStore);
|
this.dataStore = new CachingDataStore(dataStore);
|
||||||
Random ran = new Random();
|
|
||||||
systemID = ran.nextInt() & 0x00FFFFFF;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelfPeer(Peer selfPeer)
|
public synchronized void init()
|
||||||
{
|
{
|
||||||
this.selfPeer = selfPeer;
|
|
||||||
|
List<LocalData> localDataList = dataStore.getDAOForClass(LocalData.class).getAll();
|
||||||
|
if (localDataList.isEmpty())
|
||||||
|
{
|
||||||
|
localData = dataStore.getDAOForClass(LocalData.class).create(0);
|
||||||
|
}
|
||||||
|
else if (localDataList.size() == 1)
|
||||||
|
{
|
||||||
|
localData = localDataList.getFirst();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("We have more than one LocalData somehow!!");
|
||||||
|
}
|
||||||
|
if (localData.getSystemID() == 0)
|
||||||
|
{
|
||||||
|
Random ran = new Random();
|
||||||
|
localData.setSystemID(ran.nextInt() & 0x00FFFFFF);
|
||||||
|
objectChanged(localData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized Peer getSelfPeer()
|
public synchronized Peer getSelfPeer()
|
||||||
{
|
{
|
||||||
if (selfPeer == null)
|
if (localData.getLocalPeer() == null)
|
||||||
selfPeer = createObjectByType(ObjectStatements.ObjectType.OBJECT_TYPE_PEER);
|
{
|
||||||
return selfPeer;
|
localData.setLocalPeer(createObjectByType(ObjectStatements.ObjectType.OBJECT_TYPE_PEER));
|
||||||
|
objectChanged(localData);
|
||||||
|
}
|
||||||
|
return localData.getLocalPeer();
|
||||||
}
|
}
|
||||||
// private Map<Long, Node> nodes = new HashMap<>();
|
// private Map<Long, Node> nodes = new HashMap<>();
|
||||||
|
|
||||||
public synchronized NetworkObject.ObjectID getNextObjectID(ObjectStatements.ObjectType type)
|
public synchronized ObjectID getNextObjectID(ObjectStatements.ObjectType type)
|
||||||
{
|
{
|
||||||
Random ran = new Random();
|
Random ran = new Random();
|
||||||
int randomNumber = ran.nextInt();
|
int randomNumber = ran.nextInt();
|
||||||
NetworkObject.ObjectID objectID = new NetworkObject.ObjectID(type, systemID, randomNumber);
|
ObjectID objectID = new ObjectID(type, localData.getSystemID(), randomNumber);
|
||||||
System.out.println("Assigned new object ID: " + objectID);
|
System.out.println("Assigned new object ID: " + objectID);
|
||||||
return objectID;
|
return objectID;
|
||||||
}
|
}
|
||||||
|
@ -59,7 +75,7 @@ public class Model
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized <T extends NetworkObject> T createObjectByID(NetworkObject.ObjectID id)
|
public synchronized <T extends NetworkObject> T createObjectByID(ObjectID id)
|
||||||
{
|
{
|
||||||
if (id.toLong() == 0)
|
if (id.toLong() == 0)
|
||||||
throw new IllegalArgumentException("Cannot create an object with ID=0!");
|
throw new IllegalArgumentException("Cannot create an object with ID=0!");
|
||||||
|
@ -76,7 +92,7 @@ public class Model
|
||||||
return createObjectByID(getNextObjectID(type));
|
return createObjectByID(getNextObjectID(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized <T extends NetworkObject> T getObject(NetworkObject.ObjectID id)
|
public synchronized <T extends NetworkObject> T getObject(ObjectID id)
|
||||||
{
|
{
|
||||||
if (id.toLong() == 0)
|
if (id.toLong() == 0)
|
||||||
return null;
|
return null;
|
||||||
|
@ -84,7 +100,7 @@ public class Model
|
||||||
return dataStore.getDAOForClass(clazz).get(id.toLong());
|
return dataStore.getDAOForClass(clazz).get(id.toLong());
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized <T extends NetworkObject> T getOrCreateObject(NetworkObject.ObjectID id)
|
public synchronized <T extends NetworkObject> T getOrCreateObject(ObjectID id)
|
||||||
{
|
{
|
||||||
if (id.toLong() == 0)
|
if (id.toLong() == 0)
|
||||||
return null;
|
return null;
|
||||||
|
@ -138,13 +154,15 @@ public class Model
|
||||||
|
|
||||||
public ObjectChangeRecord getChangeRecord(long id)
|
public ObjectChangeRecord getChangeRecord(long id)
|
||||||
{
|
{
|
||||||
|
if (id == 0)
|
||||||
|
return null;
|
||||||
return dataStore.getDAOForClass(ObjectChangeRecord.class).get(id);
|
return dataStore.getDAOForClass(ObjectChangeRecord.class).get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyChangeRecord(ObjectChangeRecord record)
|
public void applyChangeRecord(ObjectChangeRecord record)
|
||||||
{
|
{
|
||||||
if (!record.getChangeHeads().contains(currentChange.getChangeID()))
|
if (!record.getChangeHeads().contains(localData.getCurrentChangeRecord().getChangeID()))
|
||||||
throw new IllegalStateException("Change does not apply! Valid change heads=" + record.getChangeHeads() + ", we are in state " + currentChange.getChangeID());
|
throw new IllegalStateException("Change does not apply! Valid change heads=" + record.getChangeHeads() + ", we are in state " + localData.getCurrentChangeRecord().getChangeID());
|
||||||
addChangeRecord(record);
|
addChangeRecord(record);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package moe.nekojimi.friendcloud.storage;
|
package moe.nekojimi.friendcloud.storage;
|
||||||
|
|
||||||
|
import moe.nekojimi.friendcloud.ObjectChangeRecord;
|
||||||
import moe.nekojimi.friendcloud.objects.*;
|
import moe.nekojimi.friendcloud.objects.*;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
|
@ -8,10 +9,10 @@ import org.json.JSONObject;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Modifier;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.NoSuchFileException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class StupidJSONFileStore extends DataStore
|
public class StupidJSONFileStore extends DataStore
|
||||||
|
@ -36,17 +37,21 @@ public class StupidJSONFileStore extends DataStore
|
||||||
if (daos.containsKey(clazz))
|
if (daos.containsKey(clazz))
|
||||||
return (DAO<T>) daos.get(clazz);
|
return (DAO<T>) daos.get(clazz);
|
||||||
|
|
||||||
DAO<T> ret;
|
DAO<?> ret;
|
||||||
if (clazz.equals(NetworkFile.class))
|
if (clazz.equals(NetworkFile.class))
|
||||||
ret = (DAO<T>) new NetworkFileDAO();
|
ret = new NetworkFileDAO();
|
||||||
else if (clazz.equals(NetworkFolder.class))
|
else if (clazz.equals(NetworkFolder.class))
|
||||||
ret = (DAO<T>) new NetworkFolderDAO();
|
ret = new NetworkFolderDAO();
|
||||||
else if (clazz.equals(Peer.class))
|
else if (clazz.equals(Peer.class))
|
||||||
ret = (DAO<T>) new PeerDAO();
|
ret = new PeerDAO();
|
||||||
else if (clazz.equals(PeerFileState.class))
|
else if (clazz.equals(PeerFileState.class))
|
||||||
ret = (DAO<T>) new PeerFileStateDAO();
|
ret = new PeerFileStateDAO();
|
||||||
else if (clazz.equals(NetworkFSNode.class))
|
else if (clazz.equals(NetworkFSNode.class))
|
||||||
ret = (DAO<T>) new NetworkFSNodeDAO();
|
ret = new NetworkFSNodeDAO();
|
||||||
|
else if (clazz.equals(LocalData.class))
|
||||||
|
ret = new LocalDataDAO();
|
||||||
|
else if (clazz.equals(ObjectChangeRecord.class))
|
||||||
|
ret = new ObjectChangeRecordDAO();
|
||||||
else
|
else
|
||||||
throw new UnsupportedOperationException("Requested DAO for unsupported type " + clazz.getCanonicalName());
|
throw new UnsupportedOperationException("Requested DAO for unsupported type " + clazz.getCanonicalName());
|
||||||
|
|
||||||
|
@ -202,11 +207,17 @@ public class StupidJSONFileStore extends DataStore
|
||||||
public T get(long id)
|
public T get(long id)
|
||||||
{
|
{
|
||||||
File file = new File(getNamespaceDirectory(), Long.toHexString(id) + ".json");
|
File file = new File(getNamespaceDirectory(), Long.toHexString(id) + ".json");
|
||||||
|
file.getParentFile().mkdirs();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
JSONObject json = new JSONObject(Files.readString(file.toPath()));
|
JSONObject json = new JSONObject(Files.readString(file.toPath()));
|
||||||
return jsonToObject(json);
|
return jsonToObject(json);
|
||||||
} catch (IOException e)
|
} catch (NoSuchFileException ex)
|
||||||
|
{
|
||||||
|
System.err.println("JSONFileStore: failed to find object with ID=" + id + ", expected in " + file.getAbsolutePath());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
@ -216,6 +227,7 @@ public class StupidJSONFileStore extends DataStore
|
||||||
public void update(T object)
|
public void update(T object)
|
||||||
{
|
{
|
||||||
File file = new File(getNamespaceDirectory(), Long.toHexString(object.getStorageID()) + ".json");
|
File file = new File(getNamespaceDirectory(), Long.toHexString(object.getStorageID()) + ".json");
|
||||||
|
file.getParentFile().mkdirs();
|
||||||
try(FileWriter writer = new FileWriter(file, false))
|
try(FileWriter writer = new FileWriter(file, false))
|
||||||
{
|
{
|
||||||
objectToJson(object).write(writer);
|
objectToJson(object).write(writer);
|
||||||
|
@ -255,7 +267,7 @@ public class StupidJSONFileStore extends DataStore
|
||||||
@Override
|
@Override
|
||||||
protected NetworkFile makeBlank(long id)
|
protected NetworkFile makeBlank(long id)
|
||||||
{
|
{
|
||||||
return new NetworkFile(new NetworkObject.ObjectID(id));
|
return new NetworkFile(new ObjectID(id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,7 +281,7 @@ public class StupidJSONFileStore extends DataStore
|
||||||
@Override
|
@Override
|
||||||
protected NetworkFolder makeBlank(long id)
|
protected NetworkFolder makeBlank(long id)
|
||||||
{
|
{
|
||||||
return new NetworkFolder(new NetworkObject.ObjectID(id));
|
return new NetworkFolder(new ObjectID(id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,7 +295,7 @@ public class StupidJSONFileStore extends DataStore
|
||||||
@Override
|
@Override
|
||||||
protected Peer makeBlank(long id)
|
protected Peer makeBlank(long id)
|
||||||
{
|
{
|
||||||
return new Peer(new NetworkObject.ObjectID(id));
|
return new Peer(new ObjectID(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -328,7 +340,37 @@ public class StupidJSONFileStore extends DataStore
|
||||||
@Override
|
@Override
|
||||||
protected PeerFileState makeBlank(long id)
|
protected PeerFileState makeBlank(long id)
|
||||||
{
|
{
|
||||||
return new PeerFileState(new NetworkObject.ObjectID(id));
|
return new PeerFileState(new ObjectID(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LocalDataDAO extends JSONObjectDAO<LocalData>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected String getNamespace()
|
||||||
|
{
|
||||||
|
return "localData";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected LocalData makeBlank(long id)
|
||||||
|
{
|
||||||
|
return new LocalData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ObjectChangeRecordDAO extends JSONObjectDAO<ObjectChangeRecord>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected String getNamespace()
|
||||||
|
{
|
||||||
|
return "changes";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ObjectChangeRecord makeBlank(long id)
|
||||||
|
{
|
||||||
|
return new ObjectChangeRecord();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,24 +2,27 @@
|
||||||
package moe.nekojimi.friendcloud.tasks;
|
package moe.nekojimi.friendcloud.tasks;
|
||||||
|
|
||||||
import moe.nekojimi.friendcloud.ConnectionManager;
|
import moe.nekojimi.friendcloud.ConnectionManager;
|
||||||
|
import moe.nekojimi.friendcloud.Main;
|
||||||
|
import moe.nekojimi.friendcloud.NotificationManager;
|
||||||
|
import moe.nekojimi.friendcloud.ObjectChangeTransaction;
|
||||||
import moe.nekojimi.friendcloud.network.PeerConnection;
|
import moe.nekojimi.friendcloud.network.PeerConnection;
|
||||||
import moe.nekojimi.friendcloud.network.requests.FilePiecesRequest;
|
import moe.nekojimi.friendcloud.network.requests.FilePiecesRequest;
|
||||||
import moe.nekojimi.friendcloud.objects.NetworkFile;
|
import moe.nekojimi.friendcloud.objects.NetworkFile;
|
||||||
import moe.nekojimi.friendcloud.objects.Peer;
|
import moe.nekojimi.friendcloud.objects.Peer;
|
||||||
import moe.nekojimi.friendcloud.objects.PeerFileState;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class FileDownloadTask implements RunnableFuture<File>
|
public class FileDownloadTask implements RunnableFuture<File>
|
||||||
{
|
{
|
||||||
private final NetworkFile file;
|
private final NetworkFile file;
|
||||||
private final ConnectionManager manager;
|
private final ConnectionManager connectionManager;
|
||||||
|
|
||||||
private final long timeoutPerPieceMs = 10_000;
|
private final long timeoutPerPieceMs = 1_000;
|
||||||
private static final int MAX_DOWNLOAD_PIECES_PER_ROUND = 128;
|
private static final int MAX_DOWNLOAD_PIECES_PER_ROUND = 128;
|
||||||
|
|
||||||
private final SortedSet<Integer> missingPieceIndices = new TreeSet<>();
|
private final SortedSet<Integer> missingPieceIndices = new TreeSet<>();
|
||||||
|
@ -28,20 +31,20 @@ public class FileDownloadTask implements RunnableFuture<File>
|
||||||
private boolean failed = false;
|
private boolean failed = false;
|
||||||
private final Object waitObject = new Object();
|
private final Object waitObject = new Object();
|
||||||
|
|
||||||
public FileDownloadTask(NetworkFile file, ConnectionManager manager)
|
public FileDownloadTask(NetworkFile file, ConnectionManager connectionManager)
|
||||||
{
|
{
|
||||||
this.file = file;
|
this.file = file;
|
||||||
this.manager = manager;
|
this.connectionManager = connectionManager;
|
||||||
for (int i = 0; i < file.getPieceCount(); i++)
|
for (int i = 0; i < file.getPieceCount(); i++)
|
||||||
{
|
{
|
||||||
missingPieceIndices.add(i);
|
missingPieceIndices.add(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileDownloadTask(NetworkFile file, ConnectionManager manager, SortedSet<Integer> missingPieces)
|
public FileDownloadTask(NetworkFile file, ConnectionManager connectionManager, SortedSet<Integer> missingPieces)
|
||||||
{
|
{
|
||||||
this.file = file;
|
this.file = file;
|
||||||
this.manager = manager;
|
this.connectionManager = connectionManager;
|
||||||
missingPieceIndices.addAll(missingPieces);
|
missingPieceIndices.addAll(missingPieces);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,38 +56,46 @@ public class FileDownloadTask implements RunnableFuture<File>
|
||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
System.out.println("Starting download of file " + file.getName());
|
System.out.println("Starting download of file " + file.getName() + " (pieces: " + missingPieceIndices + ")");
|
||||||
|
// NotificationManager.Notification notification = Main.getInstance().getNotificationManager().createNotification("Streaming " + file.getName(), "Starting download...", NotificationManager.NotificationType.TRANSFER_IN_PROGRESS);
|
||||||
|
|
||||||
|
int startingPieces = missingPieceIndices.size();
|
||||||
|
|
||||||
|
String connectionLine = "";
|
||||||
|
String progressLine = "";
|
||||||
|
|
||||||
|
Peer selfPeer = Main.getInstance().getModel().getSelfPeer();
|
||||||
while (!missingPieceIndices.isEmpty() && !cancelled && !failed && !done)
|
while (!missingPieceIndices.isEmpty() && !cancelled && !failed && !done)
|
||||||
{
|
{
|
||||||
System.out.println("Need to get " + missingPieceIndices.size() + " missing pieces.");
|
System.out.println("Need to get " + missingPieceIndices.size() + " missing pieces.");
|
||||||
|
|
||||||
Map<Peer, PeerFileState> fileStates = file.getFileStates();
|
// Map<Peer, PeerFileState> fileStates = file.getFileStates();
|
||||||
|
|
||||||
// determine what nodes we can connect to
|
// determine what nodes we can connect to
|
||||||
List<PeerConnection> connections = new ArrayList<>();
|
List<PeerConnection> connections = new ArrayList<>();
|
||||||
for (PeerFileState peerFileState : fileStates.values())
|
for (Peer peer : file.getPeersWithCopy())
|
||||||
{
|
{
|
||||||
if (peerFileState.getProgress() >= 100.0)
|
if (peer == selfPeer)
|
||||||
|
continue; // yeah that's us dipshit
|
||||||
|
PeerConnection connection = connectionManager.getNodeConnection(peer);
|
||||||
|
if (connection != null)
|
||||||
{
|
{
|
||||||
try
|
System.out.println("FileDownloadTask: Will download from " + peer.getNodeName());
|
||||||
{
|
|
||||||
PeerConnection connection = manager.getNodeConnection(peerFileState.getNode());
|
|
||||||
System.out.println("FileDownloadTask: Will download from " + peerFileState.getNode().getNodeName());
|
|
||||||
connections.add(connection);
|
connections.add(connection);
|
||||||
} catch (IOException ex)
|
|
||||||
{
|
|
||||||
System.err.println("Failed to connect to peer " + peerFileState.getNode().getNodeName() + ": " + ex.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// connectionLine = "Connected to " + connections.size() + " peers.";
|
||||||
|
// notification.setBody(connectionLine + "\n" + progressLine);
|
||||||
|
// connectionLine = "Connected to " + connections.stream().map(PeerConnection::getNode).map(Peer::getNodeName).collect(Collectors.joining(", "));
|
||||||
|
|
||||||
// shuffle the connections list
|
// shuffle the connections list
|
||||||
Collections.shuffle(connections);
|
Collections.shuffle(connections);
|
||||||
|
|
||||||
if (connections.isEmpty())
|
if (connections.isEmpty())
|
||||||
{
|
{
|
||||||
System.err.println("FileDownloadTask: No peers have the file, download failed!");
|
System.err.println("FileDownloadTask: No peers have the file, download failed!");
|
||||||
|
file.notifyPieceWaiters();
|
||||||
failed = true;
|
failed = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -136,11 +147,29 @@ public class FileDownloadTask implements RunnableFuture<File>
|
||||||
System.err.println("FileDownloadTask: Request timed out.");
|
System.err.println("FileDownloadTask: Request timed out.");
|
||||||
} catch (ExecutionException | TimeoutException e)
|
} catch (ExecutionException | TimeoutException e)
|
||||||
{
|
{
|
||||||
|
future.cancel(true);
|
||||||
e.printStackTrace(System.err);
|
e.printStackTrace(System.err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// progressLine = "Have " + (startingPieces - missingPieceIndices.size()) + " / " + missingPieceIndices.size() + " pieces. (" + file.getDownloadPercentage() + "%)";
|
||||||
|
// notification.setBody(connectionLine + "\n" + progressLine);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (file.getDownloadPercentage() >= 100.0)
|
||||||
|
{
|
||||||
|
try (ObjectChangeTransaction transaction = ObjectChangeTransaction.startTransaction(connectionManager, selfPeer.getObjectID()))
|
||||||
|
{
|
||||||
|
transaction.addObjectBeforeChange(file.getObjectID());
|
||||||
|
file.addPeerWithCopy(selfPeer);
|
||||||
|
}
|
||||||
|
catch (IOException ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// notification.setBody("Finished downloading!");
|
||||||
System.out.println("FileDownloadTask: finished downloading " + file.getName() + "!");
|
System.out.println("FileDownloadTask: finished downloading " + file.getName() + "!");
|
||||||
done = true;
|
done = true;
|
||||||
synchronized (waitObject)
|
synchronized (waitObject)
|
||||||
|
|
|
@ -2,7 +2,7 @@ package moe.nekojimi.friendcloud.tasks;
|
||||||
|
|
||||||
import moe.nekojimi.friendcloud.Main;
|
import moe.nekojimi.friendcloud.Main;
|
||||||
import moe.nekojimi.friendcloud.ObjectChangeTransaction;
|
import moe.nekojimi.friendcloud.ObjectChangeTransaction;
|
||||||
import moe.nekojimi.friendcloud.objects.NetworkObject;
|
import moe.nekojimi.friendcloud.objects.ObjectID;
|
||||||
import moe.nekojimi.friendcloud.objects.Peer;
|
import moe.nekojimi.friendcloud.objects.Peer;
|
||||||
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
import moe.nekojimi.friendcloud.protos.ObjectStatements;
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ public class JoinNetworkTask implements Runnable
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
// generate new peer ID
|
// generate new peer ID
|
||||||
NetworkObject.ObjectID peerID = null;
|
ObjectID peerID = null;
|
||||||
try (ObjectChangeTransaction builder = ObjectChangeTransaction.startTransaction(Main.getInstance().getConnectionManager(), peerID))
|
try (ObjectChangeTransaction builder = ObjectChangeTransaction.startTransaction(Main.getInstance().getConnectionManager(), peerID))
|
||||||
{
|
{
|
||||||
Peer selfPeer = Main.getInstance().getModel().getSelfPeer();
|
Peer selfPeer = Main.getInstance().getModel().getSelfPeer();
|
||||||
|
|
|
@ -22,20 +22,12 @@ public class SyncWithNetworkTask implements Runnable
|
||||||
for (Peer peer : Main.getInstance().getModel().listOtherPeers())
|
for (Peer peer : Main.getInstance().getModel().listOtherPeers())
|
||||||
{
|
{
|
||||||
// open a connection
|
// open a connection
|
||||||
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(Main.getInstance().getModel().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
|
||||||
} catch (IOException e)
|
|
||||||
{
|
|
||||||
System.err.println("SyncWithNetworkTask: Couldn't connect to " + peer + ": " + e.getMessage());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
// if no peers could be contacted:
|
// if no peers could be contacted:
|
||||||
// return success (everyone's offline)
|
// return success (everyone's offline)
|
||||||
|
|
Loading…
Reference in a new issue