Projet_JAVA_P2P_STRI2A/src/clientP2P/ClientDownloadPart.java

337 lines
8.7 KiB
Java
Raw Normal View History

2020-03-22 16:55:05 +01:00
package clientP2P;
import java.util.List;
import java.util.ArrayList;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import protocolP2P.ProtocolP2PPacket;
import protocolP2P.Payload;
import protocolP2P.LoadRequest;
import protocolP2P.FilePart;
import localException.InternalError;
import localException.ProtocolError;
import localException.TransmissionError;
import localException.VersionError;
import localException.SizeError;
import localException.SocketClosed;
import remoteException.EmptyDirectory;
import remoteException.EmptyFile;
import remoteException.InternalRemoteError;
import remoteException.VersionRemoteError;
import remoteException.ProtocolRemoteError;
import remoteException.NotFound;
import remoteException.NotATracker;
import exception.LocalException;
import exception.RemoteException;
import tools.Logger;
import tools.LogLevel;
import tools.ServeErrors;
2020-03-27 16:22:42 +01:00
import tools.HostItem;
2020-03-22 16:55:05 +01:00
/** Class to download file parts.
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public abstract class ClientDownloadPart extends ServeErrors implements Runnable {
2020-03-28 16:31:24 +01:00
protected long receivedBytesCount;
2020-03-22 16:55:05 +01:00
protected List<Long> toDoTasks;
protected List<Long> pendingTasks;
protected List<Long> tasksDone;
protected volatile boolean tasksListsLock;
protected volatile boolean stop;
protected volatile boolean failed;
protected String filename;
protected volatile boolean noTask;
protected String partsSubdir;
protected static final long MAX_PARTIAL_SIZE = 4096;
protected ClientDownload manager;
protected Logger logger;
2020-03-27 16:22:42 +01:00
protected HostItem client;
2020-03-28 16:31:24 +01:00
private HostItem server;
2020-03-22 16:55:05 +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
* @param logger Logger
2020-03-27 16:22:42 +01:00
* @param client HostItem of the application
2020-03-28 16:31:24 +01:00
* @param server HostItem of the server application
2020-03-22 16:55:05 +01:00
*/
2020-03-28 16:31:24 +01:00
public ClientDownloadPart(ClientDownload manager, String filename, String partsSubdir, Logger logger, HostItem client, HostItem server) {
2020-03-22 16:55:05 +01:00
this.manager = manager;
this.partsSubdir = partsSubdir;
this.filename = filename;
this.logger = logger;
2020-03-27 16:22:42 +01:00
this.client = client;
2020-03-28 16:31:24 +01:00
this.server = server;
2020-03-22 16:55:05 +01:00
stop = false;
failed = false;
pendingTasks = new ArrayList<>();
toDoTasks = new ArrayList<>();
tasksDone = new ArrayList<>();
noTask = true;
tasksListsLock = false;
2020-03-28 16:31:24 +01:00
receivedBytesCount = 0;
}
/** receivedBytesCount getter
* @return receivedBytesCount
*/
public Long getReceivedBytesCount() {
return Long.valueOf(receivedBytesCount);
}
/** Server getter
* @return server
*/
public HostItem getServer() {
return server;
2020-03-22 16:55:05 +01:00
}
/** 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();
synchronized(manager) {
manager.notify();
}
} catch(InterruptedException e) {
try {
setStop();
synchronized(manager) {
manager.notify();
}
} catch (InterruptedException e2) {
}
}
}
writeLog("Closing socket", LogLevel.Info);
try{
closeSocket();
} catch(IOException e){
writeLog("can't close socket", LogLevel.Error);
}
}
/** Close the socket
*/
protected abstract void closeSocket() throws IOException;
/** 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);
ProtocolP2PPacket<?> p = reqPart(offset);
if (p == null) {
stop = true;
}
failed = downloadPart(p);
if (failed) {
System.err.println("Error: DownloadPart failed.");
writeLog("DownloadPart failed.", LogLevel.Error);
stop = true;
} else if (toDoTasks.isEmpty()) {
noTask = true;
}
} catch (IndexOutOfBoundsException e) {
writeLog(e, LogLevel.Error);
noTask = true;
}
}
}
/** Send a request for a specific offset.
* @param offset Offset of the file part to download
* @return ProtocolP2PPacketTCP used to send request
*/
protected ProtocolP2PPacket<?> reqPart(Long offset) {
writeLog("New request: " + offset, LogLevel.Info);
// 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) {
writeLog("reqPart interruptedException", LogLevel.Error);
return null;
}
} else {
writeLog("reqPart (offset " + offset + " not in toDoTasks)", LogLevel.Error);
return null;
}
// send request
try {
2020-03-27 16:22:42 +01:00
ProtocolP2PPacket<?> d = createProtocolP2PPacket(new LoadRequest(filename, offset.longValue(), MAX_PARTIAL_SIZE, client));
2020-03-22 16:55:05 +01:00
d.sendRequest(getSocket());
return d;
} catch (InternalError e) {
writeLog("reqPart internalError", LogLevel.Error);
return null;
} catch (IOException e) {
writeLog("reqPart ioexception", LogLevel.Error);
writeLog(e, LogLevel.Error);
return null;
} catch (SocketClosed e){
writeLog("reqPart SocketClosed", LogLevel.Error);
return null;
}
}
/** Get the socket */
protected abstract Object getSocket();
/** Download file part associated to the request send (d).
* @param d request packet
* @return true on failure, else false
*/
public < T extends ProtocolP2PPacket<?> > boolean downloadPart(T d) {
if (d == null) {
writeLog("downloadPart -> d is null.", LogLevel.Error);
return true;
}
try {
Payload p = d.receiveResponse().getPayload();
assert p instanceof FilePart : "This payload must be instance of FilePart";
if (!(p instanceof FilePart)) {
writeLog("cannot get size.", LogLevel.Error);
return true;
} else {
FilePart fp = (FilePart)p;
if (!fp.getFilename().equals(filename)) {
writeLog("wrong file received: `" + fp.getFilename() + "`", LogLevel.Error);
return true;
}
Long offset = Long.valueOf(fp.getOffset());
if (pendingTasks.contains(offset)) {
2020-03-28 16:31:24 +01:00
byte[] partialContent = fp.getPartialContent();
2020-03-22 16:55:05 +01:00
try {
2020-03-28 16:31:24 +01:00
Files.write(new File(partsSubdir + filename + "_" + offset + ".part").toPath(), partialContent);
receivedBytesCount += partialContent.length;
2020-03-22 16:55:05 +01:00
} catch (IOException e) {
writeLog("cannot write file (" + partsSubdir + filename + "_" + offset + ".part)", LogLevel.Error);
}
} else {
writeLog("wrong file part received.", LogLevel.Error);
return true;
}
try {
synchronized(this) {
while(tasksListsLock) {
this.wait();
}
tasksListsLock = true;
pendingTasks.remove(offset);
tasksDone.add(offset);
tasksListsLock = false;
this.notifyAll();
}
} catch(InterruptedException e) {
writeLog("DownloadPart Interrupted exception", LogLevel.Error);
return true;
}
}
} catch (LocalException e) {
writeLog(e, LogLevel.Error);
return true;
} catch (RemoteException e) {
writeLog(e, LogLevel.Error);
return true;
} catch (IOException e) {
System.err.println("Error: downloadPart ioexception");
writeLog("downloadPart ioexception", LogLevel.Error);
return true;
}
return false;
}
}