Projet_JAVA_P2P_STRI2A/src/clientP2P/ClientDownloadPartUDP.java

348 lines
10 KiB
Java
Raw Normal View History

2020-03-12 17:52:31 +01:00
package clientP2P;
import java.util.List;
import java.util.ArrayList;
import java.net.DatagramSocket;
import protocolP2P.ProtocolP2PPacketUDP;
import protocolP2P.Payload;
import protocolP2P.LoadRequest;
import protocolP2P.FilePart;
2020-03-19 13:48:39 +01:00
import localException.InternalError;
2020-03-12 17:52:31 +01:00
import remoteException.EmptyDirectory;
import remoteException.EmptyFile;
2020-03-19 13:48:39 +01:00
import localException.ProtocolError;
2020-03-12 17:52:31 +01:00
import remoteException.InternalRemoteError;
import remoteException.VersionRemoteError;
2020-03-19 13:48:39 +01:00
import localException.TransmissionError;
2020-03-12 17:52:31 +01:00
import remoteException.ProtocolRemoteError;
2020-03-19 13:48:39 +01:00
import localException.VersionError;
import localException.SizeError;
2020-03-12 17:52:31 +01:00
import remoteException.NotFound;
2020-03-20 11:27:57 +01:00
import remoteException.NotATracker;
2020-03-12 17:52:31 +01:00
import java.nio.file.Files;
import java.io.File;
import java.io.IOException;
2020-03-19 17:49:39 +01:00
import tools.Logger;
import tools.LogLevel;
2020-03-12 17:52:31 +01:00
/** Class to download file parts on udp.
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class ClientDownloadPartUDP implements Runnable {
private List<Long> toDoTasks;
private List<Long> pendingTasks;
private List<Long> tasksDone;
private volatile boolean tasksListsLock;
private volatile boolean stop;
private volatile boolean failed;
private String filename;
private DatagramSocket socket;
private volatile boolean noTask;
private String partsSubdir;
private static final long MAX_PARTIAL_SIZE = 4096;
2020-03-16 15:56:47 +01:00
private ClientDownloadUDP manager;
2020-03-19 17:49:39 +01:00
private Logger logger;
2020-03-12 17:52:31 +01:00
/** Constructor with filename, socket, and part subdir
* @param filename name of file to download
* @param socket socket to use
* @param partsSubdir directory to store .part files
*/
2020-03-19 17:49:39 +01:00
public ClientDownloadPartUDP(ClientDownloadUDP manager, String filename, DatagramSocket socket, String partsSubdir, Logger logger) {
2020-03-16 15:56:47 +01:00
this.manager = manager;
2020-03-12 17:52:31 +01:00
this.partsSubdir = partsSubdir;
this.filename = filename;
this.socket = socket;
2020-03-19 17:49:39 +01:00
this.logger = logger;
2020-03-12 17:52:31 +01:00
stop = false;
failed = false;
pendingTasks = new ArrayList<>();
toDoTasks = new ArrayList<>();
tasksDone = new ArrayList<>();
noTask = true;
tasksListsLock = false;
}
/** True if thread has failed to get a file.
* @return true if thread has failed to get a file
*/
public boolean hasFailed() {
return failed;
}
/** Asks to stop thread.
* @throws InterruptedException
*/
public synchronized void setStop() throws InterruptedException {
stop = true;
this.notifyAll();
}
/** Runnable implementation */
public void run() {
while(!stop) {
try {
doTasks();
2020-03-16 15:56:47 +01:00
synchronized(manager) {
manager.notify();
}
2020-03-12 17:52:31 +01:00
} catch(InterruptedException e) {
try {
setStop();
2020-03-16 15:56:47 +01:00
synchronized(manager) {
manager.notify();
}
2020-03-12 17:52:31 +01:00
} catch (InterruptedException e2) {
}
}
}
System.err.println("Closing socket");
2020-03-19 17:49:39 +01:00
logger.writeUDP("Closing socket", LogLevel.Info);
2020-03-12 17:52:31 +01:00
socket.close();
}
/** Get list of offsets that have not be downloaded if failed, else
* empty list.
* @return list of offsets
*/
public List<Long> getFailed() {
List<Long> ret = new ArrayList<>();
if (failed) {
ret.addAll(pendingTasks);
ret.addAll(toDoTasks);
}
return ret;
}
/** Get list of downloaded file parts offset, then clear this list.
* @return list of offsets
* @throws InterruptedException
*/
public List<Long> getDone() throws InterruptedException {
if (tasksDone.size() == 0) {
return new ArrayList<>();
} else {
synchronized (this) {
while(tasksListsLock) {
this.wait();
}
tasksListsLock = true;
List<Long> ret = new ArrayList<>(tasksDone);
tasksDone.clear();
tasksListsLock = false;
this.notifyAll();
return ret;
}
}
}
/** Adds offset of files parts to download.
* @param task offset to download
* @throws InterruptedException
*/
public synchronized void assignTask(Long task) throws InterruptedException {
synchronized(this) {
while(tasksListsLock) {
this.wait();
}
tasksListsLock = true;
toDoTasks.add(task);
noTask = false;
tasksListsLock = false;
this.notifyAll();
}
}
/** Send one request and wait for one response. Blocks when no task.
* @throws InterruptedException
*/
public synchronized void doTasks() throws InterruptedException {
while(noTask && !stop) {
this.wait();
}
if (!stop) {
try {
Long offset = toDoTasks.get(0);
2020-03-22 13:44:08 +01:00
ProtocolP2PPacketUDP<LoadRequest> p = reqPart(offset);
2020-03-12 17:52:31 +01:00
if (p == null) {
stop = true;
}
2020-03-19 13:48:39 +01:00
2020-03-12 17:52:31 +01:00
failed = downloadPart(p);
if (failed) {
System.err.println("Error: DownloadPart failed.");
stop = true;
} else if (toDoTasks.isEmpty()) {
noTask = true;
}
} catch (IndexOutOfBoundsException e) {
2020-03-19 17:49:39 +01:00
logger.writeUDP(e, LogLevel.Error);
2020-03-12 17:52:31 +01:00
noTask = true;
}
2020-03-19 13:48:39 +01:00
2020-03-12 17:52:31 +01:00
}
}
2020-03-19 13:30:49 +01:00
/** Send a request for a specific offset.
* @param offset Offset of the file part to download
* @return ProtocolP2PPacketTCP used to send request
*/
2020-03-22 13:44:08 +01:00
private ProtocolP2PPacketUDP<LoadRequest> reqPart(Long offset) {
2020-03-12 17:52:31 +01:00
System.err.println("New request: "+ offset);
2020-03-19 17:49:39 +01:00
logger.writeUDP("New request: "+ offset, LogLevel.Info);
2020-03-12 17:52:31 +01:00
// maintain tracking of tasks
if (toDoTasks.contains(offset)) {
try {
synchronized (this) {
while(tasksListsLock) {
this.wait();
}
tasksListsLock = true;
toDoTasks.remove(offset);
pendingTasks.add(offset);
tasksListsLock = false;
this.notifyAll();
}
} catch(InterruptedException e) {
System.err.println("Error: reqPart interruptedException");
2020-03-19 17:49:39 +01:00
logger.writeUDP("reqPart interruptedException", LogLevel.Error);
2020-03-12 17:52:31 +01:00
return null;
}
} else {
System.err.println("Error: reqPart (offset " + offset + " not in toDoTasks)");
2020-03-19 17:49:39 +01:00
logger.writeUDP("reqPart (offset " + offset + " not in toDoTasks)", LogLevel.Error);
2020-03-12 17:52:31 +01:00
return null;
}
// send request
try {
2020-03-22 13:44:08 +01:00
ProtocolP2PPacketUDP<LoadRequest> d = new ProtocolP2PPacketUDP<>(new LoadRequest(filename, offset.longValue(), MAX_PARTIAL_SIZE));
2020-03-12 17:52:31 +01:00
d.sendRequest((Object)socket);
return d;
} catch (InternalError e) {
System.err.println("Error: reqPart internalError");
2020-03-19 17:49:39 +01:00
logger.writeUDP("reqPart internalError", LogLevel.Error);
2020-03-12 17:52:31 +01:00
return null;
} catch (IOException e) {
e.printStackTrace();
System.err.println("Error: reqPart ioexception");
2020-03-19 17:49:39 +01:00
logger.writeUDP("reqPart ioexception", LogLevel.Error);
2020-03-12 17:52:31 +01:00
return null;
}
}
2020-03-19 13:30:49 +01:00
/** Download file part associated to the request send (d).
* @param d request packet
* @return true on failure, else false
*/
2020-03-22 13:44:08 +01:00
public boolean downloadPart(ProtocolP2PPacketUDP<LoadRequest> d) {
2020-03-12 17:52:31 +01:00
if (d == null) {
System.err.println("Error: downloadPart -> d is null.");
2020-03-19 17:49:39 +01:00
logger.writeUDP("downloadPart -> d is null.", LogLevel.Error);
2020-03-12 17:52:31 +01:00
return true;
}
try {
Payload p = d.receiveResponse().getPayload();
assert p instanceof FilePart : "This payload must be instance of FilePart";
if (!(p instanceof FilePart)) {
System.err.println("Error: cannot get size.");
2020-03-19 17:49:39 +01:00
logger.writeUDP("cannot get size.", LogLevel.Error);
2020-03-12 17:52:31 +01:00
return true;
} else {
FilePart fp = (FilePart)p;
if (!fp.getFilename().equals(filename)) {
System.err.println("Error: wrong file received: `" + fp.getFilename() + "`");
2020-03-19 17:49:39 +01:00
logger.writeUDP("wrong file received: `" + fp.getFilename() + "`", LogLevel.Error);
2020-03-12 17:52:31 +01:00
return true;
}
Long offset = Long.valueOf(fp.getOffset());
if (pendingTasks.contains(offset)) {
try {
Files.write(new File(partsSubdir + filename + "_" + offset + ".part").toPath(), fp.getPartialContent());
} catch (IOException e) {
System.err.println("Error: cannot write file (" + partsSubdir + filename + "_" + offset + ".part)");
2020-03-19 17:49:39 +01:00
logger.writeUDP("cannot write file (" + partsSubdir + filename + "_" + offset + ".part)", LogLevel.Error);
2020-03-12 17:52:31 +01:00
}
} else {
System.err.println("Error: wrong file part received.");
2020-03-19 17:49:39 +01:00
logger.writeUDP("wrong file part received.", LogLevel.Error);
2020-03-12 17:52:31 +01:00
return true;
}
try {
synchronized(this) {
while(tasksListsLock) {
this.wait();
}
tasksListsLock = true;
pendingTasks.remove(offset);
tasksDone.add(offset);
tasksListsLock = false;
this.notifyAll();
}
} catch(InterruptedException e) {
System.err.println("Error: DownloadPart Interrupted exception");
2020-03-19 17:49:39 +01:00
logger.writeUDP("DownloadPart Interrupted exception", LogLevel.Error);
2020-03-12 17:52:31 +01:00
return true;
}
}
} catch (EmptyDirectory e) {
System.err.println("Error: empty directory.");
2020-03-19 17:49:39 +01:00
logger.writeUDP("empty directory.", LogLevel.Error);
2020-03-12 17:52:31 +01:00
return true;
} catch (EmptyFile e) {
System.err.println("Error: downloadPart emptyFile");
2020-03-19 17:49:39 +01:00
logger.writeUDP("downloadPart emptyFile", LogLevel.Error);
2020-03-12 17:52:31 +01:00
// TODO: use more specific errors
return true;
} catch (ProtocolError e) {
System.err.println("Error: downloadPart protocolError");
2020-03-19 17:49:39 +01:00
logger.writeUDP("downloadPart protocolError", LogLevel.Error);
2020-03-12 17:52:31 +01:00
return true;
} catch (InternalRemoteError e) {
System.err.println("Error: downloadPart internalRemoteError");
2020-03-19 17:49:39 +01:00
logger.writeUDP("downloadPart internalRemoteError", LogLevel.Error);
2020-03-12 17:52:31 +01:00
return true;
} catch (VersionRemoteError e) {
System.err.println("Error: downloadPart versionRemoteError");
2020-03-19 17:49:39 +01:00
logger.writeUDP("downloadPart versionRemoteError", LogLevel.Error);
2020-03-12 17:52:31 +01:00
return true;
} catch (ProtocolRemoteError e) {
System.err.println("Error: downloadPart protocolRemoteError");
2020-03-19 17:49:39 +01:00
logger.writeUDP("downloadPart protocolRemoteError", LogLevel.Error);
2020-03-12 17:52:31 +01:00
return true;
} catch (TransmissionError e) {
System.err.println("Error: downloadPart transmissionError");
2020-03-19 17:49:39 +01:00
logger.writeUDP("downloadPart transmissionError", LogLevel.Error);
2020-03-12 17:52:31 +01:00
return true;
} catch (VersionError e) {
System.err.println("Error: downloadPart versionError");
2020-03-19 17:49:39 +01:00
logger.writeUDP("downloadPart versionError", LogLevel.Error);
2020-03-12 17:52:31 +01:00
return true;
} catch (SizeError e) {
System.err.println("Error: downloadPart sizeError");
2020-03-19 17:49:39 +01:00
logger.writeUDP("downloadPart sizeError", LogLevel.Error);
2020-03-12 17:52:31 +01:00
return true;
} catch (NotFound e) {
System.err.println("Error: downloadPart notFound");
2020-03-19 17:49:39 +01:00
logger.writeUDP("downloadPart notFound", LogLevel.Error);
2020-03-12 17:52:31 +01:00
return true;
} catch (IOException e) {
System.err.println("Error: downloadPart ioexception");
2020-03-19 17:49:39 +01:00
logger.writeUDP("downloadPart ioexception", LogLevel.Error);
2020-03-12 17:52:31 +01:00
return true;
} catch (InternalError e) {
System.err.println("Error: downloadPart internalError");
2020-03-19 17:49:39 +01:00
logger.writeUDP("downloadPart internalError", LogLevel.Error);
2020-03-12 17:52:31 +01:00
return true;
2020-03-20 11:27:57 +01:00
} catch (NotATracker e) {
System.err.println("Error: downloadPart notATracker");
logger.writeUDP("downloadPart notATracker", LogLevel.Error);
return true;
2020-03-12 17:52:31 +01:00
}
return false;
}
}